Skip to main content
Every order and position request is signed with an API key. There is no login and no token to refresh: you compute a signature over the request and send it in three headers.

Get a key

API keys are issued by Splash on request. Ask your Splash contact for one. You receive two values:
  • the key id, sent in SPLASH-ACCESS-KEY;
  • the secret, used to sign requests. It is shown to you once; keep it private and never send it.
Use the secret exactly as issued: its characters, as a UTF-8 string, are the HMAC key. It looks like base64, but do not base64-decode it. Keys never expire. A key stays valid until it is revoked, and Splash revokes a key on request: ask your Splash contact.

The three headers

Also send Content-Type: application/json on any request with a body. A signed request whose body is not JSON is rejected with 415 UNSUPPORTED_MEDIA_TYPE.

What you sign

The message is four strings joined with nothing between them: The path begins with /predictions-service/api, because that is part of the URL. For example:
  • POST https://api.splashsports.com/predictions-service/api/trading/orders signs the path /predictions-service/api/trading/orders.
  • GET .../orders?request_id=abc signs /predictions-service/api/orders?request_id=abc.
Sign the exact bytes you send. If you build the JSON, sign it, and then let your HTTP library re-serialize it, the spacing or key order can change and the signature will not match. Serialize once, sign that string, and send that same string.
Then compute the HMAC-SHA256 of the message with your secret as the key and base64-encode the 32-byte result. The key is the secret string exactly as issued, encoded as UTF-8; do not base64-decode it first.

Example: place a signed order

POST /trading/orders with a signed request. Pick your language; the choice carries to the other examples on this page.
--data-binary (cURL), the body string (TypeScript) and the marshalled bytes (Go) send exactly what was signed.

Example: a signed GET

A request with no body signs the empty string, and carries the query string in the signed path.

Check your signing

If you get Invalid signature, compare your code against this fixed input. With
  • secret s3cret
  • timestamp 1700000000000
  • method POST
  • path /predictions-service/api/trading/orders
  • body {"a":1}
the message is 1700000000000POST/predictions-service/api/trading/orders{"a":1} and the signature must be:
Run your sign function on those inputs and compare. openssl computes the same value:
If you get a different value, check that you used the secret as a plain string (not base64-decoded), the method in upper case, and the path with /predictions-service/api.

Authentication errors

A failed check is 401 with error: "NOT_AUTHORIZED" and one of these messages: API key revoked is reported only when the signature is correct, so a wrong secret always reads Invalid signature. A failed check happens before anything else, so on POST /trading/orders nothing is recorded: fix the signing and send the order again with the same request_id.

Rate limits

Requests are limited to 10 per second per IP address; see Rate limits for handling a 429 Too Many Requests and a retrying request helper in each language.