INDEPENDENT DEVELOPER REFERENCE
DraftKings API errors and rate limits
What each status means, what counts against your plan, when limits reset, and how to write retry logic that does not waste quota.
Editorial review: · Not a live coverage report
The error envelope
Errors return JSON with a numeric code and a message. Validation failures can add details. Log the status and message, and never the Authorization header.
{"error":{"code":429,"message":"Account request limit reached"}}
{"error":{"code":400,"message":"Invalid input","details":["..."]}}What each status means
Each status has one sensible response. Retrying is only right for two of them.
| Status | Meaning | What to do |
|---|---|---|
| 400 | Invalid, unsupported or duplicate parameter, or a value out of bounds | Fix the query. Do not retry. |
| 401 | Missing, malformed, invalid or revoked key | Check the Authorization: Bearer dk_... header and that the key is active. |
| 403 | Rejected origin, a plan-gated delivery feature, or the DFS access boundary | Read the message. Upgrade or stop. Do not work around it. |
| 404 | Unknown resource path | Check the path against the docs. |
| 409 | Key or capture conflict, including a sixth active key or a stream cursor ahead of retained changes | Revoke a key, or restart from a current snapshot. |
| 410 | Stream cursor purged | Take a new snapshot and reconnect. |
| 413 | Stream snapshot over its bound | Add a sport filter. |
| 428 | Stream opened without a cursor | Take a snapshot first, or send start=latest on purpose. |
| 429 | Monthly or per-minute allowance reached | Wait for the relevant UTC boundary. |
| 503 | Storage or a dataset is unavailable, or a delivery feature is disabled | Back off, then check status. Do not assume zero rows. |
Plan limits
Free includes 500 requests/month at 20/minute; Developer is $49/month with 10,000 requests/month at 60/minute; Production is $149/month with 50,000 requests/month at 120/minute. Limits apply to the account, shared across every key. Existing legacy accounts keep their original daily terms, and the dashboard shows your active contract.
| Plan | Price | Requests per UTC month | Requests per minute | Pick6 stream | Signed webhooks |
|---|---|---|---|---|---|
| Free | $0/month | 500 | 20 | No | No |
| Developer | $49/month | 10,000 | 60 | Yes | No |
| Production | $149/month | 50,000 | 120 | Yes | Yes |
What counts as a request
One authenticated API call is one request, whatever its page size. Authentication runs first, so these all count: empty results, a dataset returning 503, and invalid queries that passed authentication.
- Each stream connection counts as one request, and connections last up to 55 seconds.
- Each stream snapshot counts as one request.
- A failed key check does not reach your account, because the key was not recognized.
/api/healthand/api/openapiare public and need no key.
When limits reset
The two limits work on different clocks. Both use UTC, and neither follows your invoice date. Requests do not roll over.
| Limit | Resets | Typical cause of a 429 |
|---|---|---|
| Per minute | At the next UTC minute | A burst, such as parallel page fetches |
| Per month | At the next UTC calendar month | Steady polling that outruns the allowance |
| Legacy daily | At the next UTC day | Legacy accounts only |
The 429 message does not say which limit you reached, and responses carry no rate-limit or remaining-quota headers. Check the dashboard for current usage. A simple rule works in code: on a first 429, sleep to the next UTC minute and try once. If it fails again, stop the job and alert yourself.
Retry logic that respects quota
Retry 503 and network failures a few times with exponential backoff. Handle 429 by waiting for a boundary. Every other status needs a code or account change, so retrying only spends requests.
import time
from datetime import datetime, timezone
def seconds_to_next_minute():
now = datetime.now(timezone.utc)
return 60 - now.second + 1
def fetch_with_policy(call):
limited = False
for attempt in range(1, 4):
status, body = call()
if status < 400:
return body
if status == 429 and not limited:
limited = True
time.sleep(seconds_to_next_minute())
continue
if status == 503 and attempt < 3:
time.sleep(2 ** attempt)
continue
raise RuntimeError(f"{status}: {body['error']['message']}")
raise RuntimeError("Gave up after retries")Do not bypass a denial
A 403 on DFS or a delivery feature is a real boundary. Do not change hosts, share keys between accounts or scrape the source as a fallback. A 503 is not permission to invent data: return an explicit unavailable state to your own users, with the last observed_at you hold.
Try it
Send a deliberately invalid limit to see a validation error, and note that it spends one request.
curl --fail-with-body "https://draftkingsapi.com/api/v1/odds?limit=101" \
-H "Authorization: Bearer $DRAFTKINGS_API_KEY"Endpoint docs
Working assets
Check the recorded data
Questions
Does the API send rate-limit headers?
No. Responses do not carry remaining-quota headers. The dashboard shows your active allowance and reset, so count requests in your own client if you need live tracking.
Do multiple keys give me more requests?
No. Limits apply to the account, shared across all active keys.
Do failed requests count?
Valid authenticated calls count even if empty or if a dataset is unavailable. Invalid queries that authenticate before validation also count. Calls with an unrecognized key do not reach an account.
When does my monthly allowance reset?
At the start of the UTC calendar month, not on your invoice date. Unused requests do not roll over.
Related resources
Your account limit was reached. Wait for the next UTC minute, and if it persists the UTC month. Avoid tight retry loops, since keys share one budget.
Why am I getting a 401 or 403?A 401 means the key is missing, malformed or revoked. A 403 means an origin, plan or redistribution boundary, not a problem with the key.
What does a 503 or an empty page mean?A 503 means storage or a dataset is not available. An empty page means the query matched nothing. Neither should be read as zero offers.
DraftKings API pagination: offset, after and cursorsThree pagination styles with working loops: offset lists, the Pick6 next_after sequence and statistics cursors, plus their limits.
pricingIndependent service operated by DD Inc., not affiliated with or endorsed by DraftKings. Technical access is not a redistribution license. No wagering: this service does not place, recommend or settle entries or bets. Check sources and status for actual capture context.