Errors
The Sharpe API uses RFC 9457 Problem Details for error responses. Every error is a flat JSON object with a machine-readable type URI, a human-readable title, the HTTP status code, a detailed message, and the request ID for debugging.
Error format
Every error response is a flat RFC 9457 Problem Details object at the top level. There is no ok field or nested error wrapper.
Properties
- Name
type- Type
- string
- Description
A URI reference that identifies the error type (e.g.,
https://www.sharpe.ai/errors/invalid_parameter). Use this for programmatic error handling.
- Name
title- Type
- string
- Description
A short, human-readable summary of the error.
- Name
status- Type
- integer
- Description
The HTTP status code.
- Name
detail- Type
- string
- Description
A human-readable explanation of what went wrong and how to fix it.
- Name
request_id- Type
- string
- Description
Unique request identifier (e.g.,
req_abc123def456ghij). Include this when contacting support.
- Name
doc_url- Type
- string
- Description
Link to the relevant documentation page for troubleshooting this error.
- Name
suggested_action- Type
- string
- Description
Actionable guidance on how to resolve this error.
Error response
{
"type": "https://www.sharpe.ai/errors/invalid_parameter",
"title": "Invalid Parameter",
"status": 400,
"detail": "Invalid parameter \"type\": Invalid enum value. Expected 'current' | 'accumulated' | 'history'",
"request_id": "req_abc123def456ghij",
"doc_url": "https://www.sharpe.ai/docs/errors#error-codes",
"suggested_action": "Check the parameter value against the endpoint documentation. Use GET /v1/futures/coins or GET /v1/meta/coverage to discover valid values."
}
Error codes
Here is the complete list of error types returned by the Sharpe API. Use the type URI for programmatic branching and the status code for broad categorization.
- Name
invalid_api_key- Type
- 401
- Description
The provided API key is malformed or has been revoked. Double-check your
Authorization: BearerorX-API-Keyheader.
- Name
expired_api_key- Type
- 401
- Description
The API key has expired. Generate a new key from the Sharpe dashboard.
- Name
insufficient_scope- Type
- 403
- Description
Your API key does not have the required scope to access this resource. Check the scopes assigned to your key.
- Name
monthly_quota_exceeded- Type
- 429
- Description
You have exhausted your monthly request quota. Retryable: wait for the reset in the
Retry-Afterheader, or upgrade your plan.
- Name
invalid_parameter- Type
- 400
- Description
A query parameter has an invalid value. Check the
detailfield for specifics.
- Name
missing_parameter- Type
- 400
- Description
A required query parameter is missing from the request.
- Name
rate_limit_exceeded- Type
- 429
- Description
You have exceeded your per-minute rate limit. Wait until the time indicated by the
X-RateLimit-Resetheader.
- Name
resource_not_found- Type
- 404
- Description
The requested endpoint or resource does not exist. Verify the URL path.
- Name
internal_error- Type
- 500
- Description
An unexpected error occurred on our side. These are automatically reported to our team.
- Name
upstream_error- Type
- 502
- Description
A dependency failed to respond. Used only by endpoints that do not yet carry the freshness envelope (their reference pages list
502). Retry with exponential backoff.
- Name
service_unavailable- Type
- 503
- Description
A data source or our data store is temporarily unavailable, and nothing stored could be served instead. The
detailnames the source (for exampleGoPlus Security is unavailable.), andRetry-Aftersays when to try again:60seconds for an upstream source,30for the data store. Every endpoint that carries the freshness envelope answers an outage this way; 502 is not used there.
An answer the server could only partly build is not an error: it is a 200 whose meta.runtime_status is degraded (with X-Data-Partial naming the reads that were cut short, or X-Data-Fallback when the server's last good answer or a stored snapshot stands in). Degraded answers are never cached, so a retry after a short wait can return the complete one. See Data freshness.
Retry guidance
Not all errors are retryable. Here is how to handle each category:
4xx client errors: do not retry
These indicate a problem with your request. Fix the issue before retrying.
- 400: Fix the invalid or missing parameter
- 401: Check your API key is valid and not expired
- 403: Create a key with the scopes this endpoint requires
- 404: Verify the URL
429 rate limit and quota: retry after the reset
Both limit rejections are retryable and both return 429. Use Retry-After when present, or X-RateLimit-Reset for the per-minute window:
rate_limit_exceeded: the per-minute window is exhausted. It reopens atX-RateLimit-Reset.monthly_quota_exceeded: the monthly quota is exhausted. It reopens atX-Quota-Reset, andRetry-Aftercarries the seconds until then.
429 response headers
HTTP/2 429
x-ratelimit-limit: 30
x-ratelimit-remaining: 0
x-ratelimit-reset: 1743062472
retry-after: 12
x-request-id: req_abc123def456
Wait for Retry-After seconds, or until the Unix timestamp in X-RateLimit-Reset, before sending the next request.
5xx server errors: exponential backoff
For 500, 502, and 503 errors, retry with exponential backoff (and never sooner than Retry-After when it is present):
- Wait 1 second, then retry
- Wait 2 seconds, then retry
- Wait 4 seconds, then retry
- Continue doubling up to a maximum of 30 seconds
- After 5 retries, stop and alert your monitoring system
Always include the X-Request-Id from the failed response when contacting support.
This lets us trace the exact request through our infrastructure.
Troubleshooting
"My key works in cURL but fails in my app"
The most common cause is the Authorization header being stripped or malformed. Check that:
- The header is exactly
Authorization: Bearer sk_live_...with a single space afterBearer - Your HTTP client is not URL-encoding the header value
- No proxy or CDN between your app and
www.sharpe.aiis stripping theAuthorizationheader. If one is, useX-API-Keyinstead
"I get 403 but my plan should include this endpoint"
A 403 is always insufficient_scope: the key was created with restricted scopes. Generate a new key with the required scope, or contact support to update scopes on the existing key. Quota exhaustion is not a 403. It returns a retryable 429.
"I keep hitting 429 even at low volume"
A 429 can mean two things. Check the type field in the error response:
rate_limit_exceeded: Rate limits are per-minute fixed windows. If you send 30 requests in 2 seconds (within a single 60-second window), you will exhaust a Free tier limit instantly. Spread requests evenly across the minute or implement the backoff pattern from the rate limits guide.monthly_quota_exceeded: You have hit your plan's monthly request limit, so every request fails until the quota resets. Check your usage atGET /v1/usage, then either wait out theRetry-Afterwindow or upgrade your plan.
"I get 502 or 503 intermittently"
These mean an upstream dependency (a data source or our data store) is temporarily unavailable and nothing stored could be served instead. Wait for Retry-After, then retry with exponential backoff. If the error persists for more than 5 minutes, contact support with the X-Request-Id.
"Response data is empty or stale"
Some endpoints depend on cron jobs that populate data on a schedule (for example, current funding rates every 5 minutes and the heatmap cache every 3 hours and narrative caches every 4 hours). If you query immediately after a new endpoint is deployed, data may not yet be populated. Read the freshness envelope below: it states the data's age and whether it is past its SLA.
Data freshness
Every endpoint served by the freshness envelope (all of v1 except /v1/funding/rates, /v1/funding/coins, /v1/futures/data, /v1/futures/coins, /v1/market/derivatives-overview, /v1/arbitrage/spot-perp, /v1/arbitrage/cross-exchange, /v1/rug-check/trending, /v1/analytics/query and the system routes) says how fresh its answer is, in meta and in headers. A successful answer that is stale or incomplete is still a 200: read these fields instead of the status code.
- Name
meta.as_of- Type
- string | null
- Description
When the data was current: the dataset's own time (the newest live row, the snapshot or refresh time, or the fetch time), never the response time.
nullwhen unknown.
- Name
meta.stale_after- Type
- string | null
- Description
as_ofplus the freshness SLA. Past this instant the data is stale, however long a copy was cached: re-judge a stored copy against it.
- Name
meta.freshness_sla_seconds- Type
- integer
- Description
The dataset's SLA: its writer's cadence plus one skipped run.
GET /v1/meta/coveragelists each product's datasets with their cadence and SLA.
- Name
meta.freshness_status- Type
- string
- Description
freshwithin the SLA,stalepast it,unknownwhenas_ofisnull, judged when the answer was built (meta.generated_at).
- Name
meta.runtime_status- Type
- string
- Description
degradedwhen this read was incomplete: a leg timed out or failed, a row budget was hit (meta.truncated), or a fallback answered (meta.served_from: "last-good"or a stored snapshot).okotherwise.
- Name
meta.dataset_id, served_from, source, coverage, warnings, truncated, generated_at- Type
- various
- Description
The dataset read, where the rows came from, the upstream sources behind them, how complete the stored data is (
coverage.usedofcoverage.expected, drops by reason), what the read could not do, and when the answer was built.
The same facts travel as headers on every free and v1 answer of these endpoints: X-Data-As-Of, X-Data-Stale-After, X-Data-Stale (true unless fresh), X-Data-Status (fresh, stale, degraded or fallback), X-Freshness-Sla-Seconds, X-Data-Source (comma-separated source ids) and X-Data-Coverage (used/expected). Free endpoints whose body is a JSON object carry the fields in their own meta key too; a bare-array body has the headers only.