Getting started · Authentication
One header, two kinds of key
Secret keys authenticate your servers. Publishable keys let widgets run in a visitor's browser without exposing anything valuable. This guide covers both, how to rotate them safely, the Business IP allowlist, and why the API answers browsers even though your secret key should never be in one.
Send the key as a Bearer token
Every request under /v2 carries your key in the Authorization header, prefixed with Bearer and a space. There is no session, no OAuth dance and no signing step on the request side: the header is the whole story. A request without it, or with a key we do not recognise, is answered with 401unauthorized.
GET /v2/fixtures/211521 HTTP/1.1
Host: api.datasportsapi.com
Authorization: Bearer dsa_live_…
Accept: application/jsoncurl -H "Authorization: Bearer $DATASPORTSAPI_API_KEY" \
"https://api.datasportsapi.com/v2/fixtures/211521"Secret keys and publishable keys
The prefix tells you which is which at a glance — useful when reviewing a pull request or scanning logs.
Secret key
dsa_live_…- Lives
- On your servers only — an environment variable such as DATASPORTSAPI_API_KEY, or a secret manager.
- Reaches
- Every endpoint your plan includes, plus the stream ticket and season exports on Business.
- Limits
- Your account's quota and per-minute rate, shared with every other key.
- If it leaks
- Revoke it in the dashboard. It stops working on the very next request.
Publishable widget key
dsa_pub_…- Lives
- In your page's HTML, on purpose. It is designed to be seen.
- Reaches
- Only what the widgets read: live scores, fixtures with their events, lineups and statistics, league seasons and standings.
- Limits
- Only from the sites you list, only while your plan includes widgets (Pro and Business). Its requests use your quota.
- If copied
- Useless elsewhere: any other site gets 403 origin_not_allowed.
Publishable keys are created in the dashboard's Widgets tab together with the list of sites allowed to use them. The widgets guide explains the embed and the origin rules in detail.
Create, rotate, revoke
Keys are created in your dashboard and displayed once, at the moment you create them. There is no way to reveal a key again later — if one is lost, create a replacement and revoke the old one.
All keys on an account share one quota and one per-minute rate. A second key does not add capacity; it gives you a separate credential per service or environment, so you can cut one off without touching the others.
Revocation is immediate. The next request with a revoked key gets a 401, and an open WebSocket connection that was opened with it is closed with code 1008.
Active keys per plan
- Free1 API key
- Starter2 API keys
- Pro5 API keys
- BusinessUnlimited API keys
Rotating without downtime
- 01
Create
Add a second key in the dashboard and copy it into your secret store.
- 02
Deploy
Roll the new key out to every service that used the old one.
- 03
Watch
Check your own logs until nothing sends the old key any more.
- 04
Revoke
Revoke the old key. Anything you missed fails loudly with 401 instead of silently.
On Free, where an account holds a single key, revoke and create in the same sitting and redeploy straight away.
CORS is open. Your secret key still stays home.
The API sends permissive CORS headers, so a fetch from a web page will technically work. That is a convenience for widgets and prototypes, not an invitation.
Anything shipped to a browser can be read by whoever loads the page: a key in a JavaScript bundle, an inline script or a mobile app binary is a published key. Someone who copies it spends your quota and sees everything your plan sees. Three patterns keep a front end fast without exposing it:
Proxy through your backend
Your server holds the key, forwards a short allowlist of paths and caches the answers. Browsers talk to you, not to us.
Embed widgets
A scoreboard, a single match or a table, powered by a publishable dsa_pub_ key that is accepted only on the domains you list.
Fan out the stream
Hold the WebSocket on your server and push to clients over your own channel. With five connections per account, one socket per visitor would not scale anyway.
// proxy.ts — a tiny allowlisted proxy in front of the API (Node 22+)
import { createServer } from "node:http";
const API = "https://api.datasportsapi.com";
const KEY = process.env.DATASPORTSAPI_API_KEY!;
const ALLOWED = [/^\/v2\/livescores$/, /^\/v2\/fixtures\/\d+$/];
createServer(async (req, res) => {
const url = new URL(req.url ?? "/", "http://localhost");
if (!ALLOWED.some((re) => re.test(url.pathname))) {
res.writeHead(404).end();
return;
}
const upstream = await fetch(API + url.pathname + url.search, {
headers: { Authorization: `Bearer ${KEY}` },
});
res.writeHead(upstream.status, {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=10", // a short cache shields your quota
});
res.end(await upstream.text());
}).listen(8787);Lock the account to your servers
- Free: not included
- Starter: not included
- Pro: not included
- Business: included
Business accounts can list the IP addresses allowed to call the API. The list lives in the dashboard and applies to every key on the account. While it is empty, every address is allowed; as soon as it has an entry, a request from anywhere else is refused with 403ip_not_allowed — and that refusal does not count against your quota.
Before you switch it on, collect every egress address that calls us: production NAT gateways, staging, CI runners and any scheduled jobs. A leaked key is then useless outside your network.
Streams and webhooks authenticate differently
WebSocket
Your key never appears in a socket URL. You exchange it for a single-use ticket with POST /v2/stream/ticket, valid for 30 seconds, and connect to the URL that comes back.
Webhooks
Here we call you, so the direction flips: each delivery is signed with your endpoint's secret in the DSA-Signature header, and your server verifies it.
When authentication fails
| Status · code | What it means |
|---|---|
| 401unauthorized | No key was sent, the key is unknown, or it has been revoked. |
| 403ip_not_allowed | The request's source address is not on your IP allowlist (Business). It does not count against your quota. |
| 403origin_not_allowed | A publishable key was used from a site it does not list, or without an Origin header. |
| 403endpoint_not_in_plan | The key is valid but the endpoint is above your plan — or outside what a widget key may read. |
| 403ws_not_available | A stream ticket was requested on a plan without the WebSocket. |
A 401 or a 403 for endpoint_not_in_plan is never charged to your quota. The full list lives in the errors guide.