Skip to main content

General

Overview

The Representational State Transfer (REST) API is a software architectural style that defines a set of constraints to be used for creating web services. It allows different software systems to communicate with each other over HTTP, just like how web browsers communicate with web servers. At Digitra.com, we utilize REST API to deliver our services. This means that you can interact with our platform programmatically, accessing and manipulating Digitra.com's functions as resources via standard HTTP methods. This makes our services widely accessible across different programming languages and platforms.

Before diving into the specifics of the Digitra.com REST API, it's important to have a basic understanding of REST API concepts. This includes knowledge of HTTP methods, status codes, endpoints, request/response structures, and more. While we won't be covering these foundational concepts in this documentation, there are many resources available online to help you get up to speed.

In this documentation, we will focus solely on the specifics of the Digitra.com REST API. We'll provide detailed information about our endpoints, request parameters, response formats, and examples to help you effectively interact with our services. By following this guide, you'll be able to leverage the full potential of Digitra.com's functions programmatically.

Server URL: https://api.digitra.com


Authentication

In all authenticated required endpoint the request need to be signed by HMAC algorithm using your API credentials.

To create API credentials, you need to have an verified account, created by Digitra.com mobile app and access your profile page at Digitra.com web application.

An API credential is formed by a API key and a client secret.

The authorization consists of sending these 3 headers:

  • digitra-api-key: "<your_api_key>" used to identify you;
  • digitra-timestamp: the current timestamp (see Timestamps Date Type) used to time out old requests;
  • digitra-signature: used to ensure that the request was generated by the credential holder. It is generated by SHA256 HMAC of a inline concatenation of request parts:
    • The digitra-timestamp header value;
    • HTTP method in upper case;
    • The request path (leading slash + path without host + query strings);
    • The JSON body (when present) encoded in a single line, without line breakers and single spaced;

The code below, in Python, and the output can help you to implement these headers:

import time
import hmac
import json
from requests import Request

request = Request(
method="POST",
url="https://api.digitra.com/v1/some-endpoint",
data=json.dumps({"key1": "value1", "key2": 123.45, "key3": False}),
params={"q_key1": "value1", "q_key2": 123.45, "q_key3": False},
)

secret = "<your_api_client_secret>".encode()
prepared_req = request.prepare()
timestamp = str(int(time.time() * 1000000))

optional_body = prepared_req.body if prepared_req.body else ""
signature_payload = f"{timestamp}{prepared_req.method}{prepared_req.path_url}{optional_body}".encode()
signature = hmac.new(secret, signature_payload, "sha256").hexdigest()

request.headers["digitra-api-key"] = "<your_api_key>"
request.headers["digitra-timestamp"] = timestamp
request.headers["digitra-signature"] = signature

print(signature_payload.decode())
print(request.headers)
'1676040464591112POST/v1/some-endpoint?q_key1=value1&q_key2=123.45&q_key3=False{"key1": "value1", "key2": 123.45, "key3": false}'
{'digitra-api-key': '<your_api_key>', 'digitra-timestamp': '1676040464591112', 'digitra-signature': 'a7ecc22546b496fb2c6c48e253dec2a1a0a4a497553e023aa4244b1f29b5f9f5'}

Based on what was explained above, using Python with a more didactic-focused code, an order creation could be done like this:

import time
import hmac
import json
import requests

# Define a create order request by following the endpoint definition in this documentation and set the API key header.
request = requests.Request(
method="POST",
url="https://api.digitra.com/v1/trade/orders",
data=json.dumps(
{
"market": "BTC-USD",
"side": "BUY",
"type": "MARKET",
"time_in_force": "FOK",
"size": "0.00001",
}
),
headers={
"content-type": "application/json",
"digitra-api-key": "a92977146953450a889ad20e7a148c60",
},
)

# Prepare the request to be able to use the properties as they will be sent.
prepared_req = request.prepare()

# Define the secret key in bytes.
secret = "ek85VGlhMlFUOF9LQUxqNW16RDFfR2hETk5rWFlPQjlVQVcxUFlZTGpvdF9BVUZBLURMTkNMcllCaU5QSEIwRHFvM3dwVDMxV3IxWmYwLVloVzdaQlE9PQ".encode()

# Define the timestamp in microseconds.
timestamp = str(int(time.time() * 1000000))

# If the request has a body, use it. Otherwise, use an empty string.
optional_body = prepared_req.body if prepared_req.body else ""

# Define the signature payload.
signature_payload = f"{timestamp}{prepared_req.method}{prepared_req.path_url}{optional_body}".encode()

# Sign the payload with the secret key.
signature = hmac.new(secret, signature_payload, "sha256").hexdigest()

# Define the timestamp and signature headers
request.headers["digitra-timestamp"] = timestamp
request.headers["digitra-signature"] = signature

# Re-prepare the request, now with the timestamp and signature headers.
prepared_req = request.prepare()

# Send the request.
session = requests.Session()
response = session.send(prepared_req)

print(signature_payload.decode())
print(request.headers)
print(response.content)
1680287517023288POST/v1/trade/orders{"market": "BTC-USD", "side": "BUY", "type": "MARKET", "time_in_force": "FOK", "size": "0.00001"}

{'content-type': 'application/json', 'digitra-api-key': 'a92977146953450a889ad20e7a148c60', 'digitra-timestamp': '1680287517023288', 'digitra-signature': '18d72aecb2c33c53641dc11a5d99c40d309180ce59f20e0659095f0a8c372061'}

b'{"result":{"id":"454256407080894349","market":"BTC-USD","side":"BUY","type":"MARKET","status":"SUBMITTING","time_in_force":"FOK","price":"31240","size":"0.00001","fee":"0","filled":"0","filled_weighted_price":"0","custom_id":"","created_at":1680287517823554,"updated_at":1680287517937525}}'

With valid credentials and valid signature, your request will be processed successfully. If any inconsistence happens, the requested endpoind will raise a HTTP 401 Unauthorized response.

These inconsistences could be:

  • Invalid or deleted API Key;
  • Invalid secret;
  • Request content incompatible with signature sent;
  • A slow request with an old timestamp;
  • Algorithm problems when generating the string concatenation with the parts of the request: bars, line breaks, spacing, parameter order, querystrings format, body format etc.
*alternative keywords:* *`x-api-key`* *`oauth`* *`authorization`* *`login`*

REST Patterns and Conventions

The API endpoints follows as much was possible the best market practices of RESTfull rules adoption. Some behaviors are well market knew conventions as the correct use of HTTP status codes and methods, other are Digitra.com's conventions. All of them are described here.

Case

Static parts of URLs (whose texts do not represent path parameters) and headers are written in lowercase with hyphen as words separator.

Body fields, path parameters names and query strings names are written in lowercase with underscore as words separator.

Enum values are written in UPPER case with underscore as words separator.

Data types

TypeDescriptionExamples
Big DecimalDecimal values, typically representing quotes or financial volumes are represented as strings to ensure the correct expression of large values without risk of rounding caused by size limitations in different programming languages. The "." point is used as a decimal separator. It starts with minus sign "-" when the value is negative."5628963.145620056", "0.00018905711", "-3598.048056922"
Small DecimalDecimal values with few integer or decimal digits are represented as number.0.5; 15.32; -2.5
TimestampsTimestamps are always handled in UTC, both in request fields and in responses. They are represented as integer numeric values in the Unix Epoch pattern (aka Unix Timestamp, Unix Time or POSIX Time) representing a time in microsseconds.1669401622935335

Timestamp generation example in Python:

from datetime import datetime

now = datetime.now()
to_timestamp = int(datetime.timestamp(now) * 1000000)
from_timestamp = datetime.fromtimestamp(to_timestamp / 1000000, tz=None)

print("Current date and time:", now)
print("Unix Timestamp from date:", to_timestamp)
print("Date from Unix Timestamp:", from_timestamp)
# Current date and time: 2022-11-25 18:40:22.935335
# Unix Timestamp from date: 1669401622935335
# Date from Unix Timestamp: 2022-11-25 18:40:22.935335

Headers

Specific Digitra.com's headers begins with digitra-. Ex: digitra-api-key. Other common well known HTTP headers are used as originally concepted. Ex: content-type, content-lenght, location etc.

HTTP methods behavior

When using HTTP Methods, the behaviors below are expected:

MethodActionIdempotentSecureBody on RequestBody in Successful Response
GETQueryYesYesNoYes
PUTReplacement (or creation if none)YesNoOptionalOptional
DELETERemoval/CancelationYesNoOptionalOptional
POSTCreationNoNoOptionalOptional
PATCHPartial update, only the sent fields will be updatedYesNoYesOptional

The availability of these methods are defined in each endpoint documentation.

HTTP status codes

When receiving HTTP status codes, they corresponds to bellow meanings and can occurs on these methods:

HTTP CodeDescription
200 OKExecution completed successfully.
201 CreatedNew instance created.
204 No ContentExecution completed successfully. There is no return payload.
400 Bad RequestThe request was badly formed (header, body, path parameter or query string) because mandatory fields not send, wrong types, non-existent properties etc.
401 UnauthorizedMissing/invalid authentication header or invalid token.
404 Not FoundThe URL points to a resource or resource instance that does not exist.
405 Method Not AllowedThe consumer tried to access the resource with an unsupported method.
406 Not AcceptableThe value of the Accept header is not contained in the media types allowed by the API.
415 Unsupported Media TypeThe operation was refused because the payload is in a format not supported by the API.
429 Too Many RequestsThe operation was refused because too many requests were made within a certain period.
500 Internal Server ErrorAn API error has occurred caused by internal problems.
503 Service UnavailableThe service is currently unavailable. Try again later.

Rate limit

To ensure fast transactions and better user experience, the access rate to REST API endpoints are limited to 10 requests per-second, per API key, shared across all methods and API endpoints .

Throttling rates are not hard limits and high global traffics (came from all clients) or from the same IP can reduce this expected rate limit. Individually hitting more than the ‘requests per-second’ defined above can cause automatic requests deny from server during a small time. Too many denies can cause API key deletion. So after check the problem in code causing excessive requests, a new one will need to be created. Your account can be blocked from API key creation in cases of successive recurrences by going beyond the defined limits. Yout IP can be blocked in case of too many requests hitting the limit coming from same IP.

When the request limit is reached, the API will response with a 429 Too Many Requests status code.

*keywords:* *`#rate`* *`#limit`* *`#throttling`* *`#quota`* *`#tps`*

Body structure

All endpoints returning single objects like the ones with id (ex: /markets/{market_id}) will return the data into a property result. Ex:

{
"result": {
"id": "BTC-USD",
"base_currency": "BTC",
...
}
}

All endpoints returning array of objects like the ones without id (ex: /markets) will return the data array into a property result. Ex:

{
"result": [
{
"id": "BTC-USD",
"base_currency": "BTC",
...
},
...
]
}

Outside the result property will be metadata as pagination and links.

Errors

When a request is unsuccessful, an error response with a status code in the range of 400 due to client errors or in the range of 500 due to server errors may return a body detailing the error.

In cases of errors generated mainly from input field validations, the content may have one or all of the fields in the example below.

{
"errors":[
{
"field": "side",
"type": "type_error.enum",
"message": "value is not a valid enumeration member; permitted: \'SELL\', \'BUY\'",
"parameters": { "enum_values": ["SELL", "BUY"] }
}]
}

In cases of errors not related to any specific field, the error message is returned as follows.

{
"message": "Forbidden"
}

Meaning:

  • field: the name of the field that has rised an error due a invalid value or that broke some business rule; can be a query string, path parameter, header or body field;
  • type: the error type in a systemic notation;
  • message: the error type described in English for a human reading;
  • parameters: a JSON object with specific parameters related with error message;

'expand' query string

Request an extra content to be appended to response. This can be used to reduce the amount of requests when you want to return some resource with their nested sub-resources.

Example:

Request: GET https://api.digitra.com/v1/markets
Response:
{
"result": [
{"id": "AAVE-BRL", ...},
{"id": "AAVE-USD",...}
]
}

Request: GET https://api.digitra.com/v1/markets/AAVE-BRL/prices
Response:
{
"result": {"price": "266.4704", "bid": "278.4704", ...}
}

Request: GET https://api.digitra.com/v1/markets/AAVE-USD/prices
Response:
{
"result": {"price": "56.6748", "bid": "56.8532", ...}
}

Request: GET https://api.digitra.com/v1/markets/?expand=PRICES
Response:
{
"result": [
{
"id": "AAVE-BRL", ...,
"prices": {"price": "266.4704", "bid":"278.3838", ...}
},
{
"id": "AAVE-USD", ...,
"prices": {"price": "56.6748", "bid": "56.8532", ...}
}
]
}

It is necessary one key/value pair for each extra content. Ex: ?expand=CONTENT_1&expand=CONTENT_2

The list of permitted sub-resource expanding is defined in each endpoint when this query string is present.

Timestamp query strings

All timestamp query strings are documented indicating only its radical. But any timestamp query string can be incremented with the suffixes .gt (greater than), .gte (greater than or equal), .lt (less than) and .lte (lesser than or equal). Thus, a query string defined in this documentation as 'time' can be used as:

  • ?time=1675902321123456 to return objects with this exact timestamp
  • ?time.gt=1675902321123456 to return objects with a timestamp greater than this.
  • ?time.gte=1675902321123456 to return objects with a timestamp greater than or equal this.
  • ?time.lt=1675902321123456 to return objects with a timestamp lesser than this.
  • ?time.lte=1675902321123456 to return objects with a timestamp lesser than or equal this.
  • ?time.gt=1675902321123456&time.lt=1675904369741852 to return objects with a timestamp between these two values.
*keywords:* *`start_time`* *`end_time`* *`time`* *`timestamp`*

Pagination

When a request return more objects than a defined limit query string or its default value, a link.more_results will be returned with the relative url to access the next set of objects.

{
"result": [...],
"links": {
"more_results": {
"method": "GET",
"href": "/v1/trade/orders?side=SELL&cursor=MTIzNDU2Nzg5&limit=20",
}
}
}

The amount of objects to be returned is set by limit query string. The reference to get the next set of objects is defined by cursorquerystring. For the sequence to be consistent, do not change the parameters present in this url.

The availability of limit and cursor query strings are defined in each endpoint. In case of not re-defined in the endpoint documentation, the default limit value will be 20 objects, max of 200.

*keywords:* *`page`* *`page_size`* *`offset`* *`limit`* *`cursor`* *`pagination`*

Changelog

2023-11-27

Added GET, POST /v1/withdrawals and GET /v1/withdrawals/{withdrawal_id} endpoints.

2023-04-24

Added base_volume_24h, quote_volume_24h, high_24h,low_24h and price_change_percent_24h to /v1/markets/{market_id}/prices endpoint.

2023-02-10

First release.