API Reference Overview
Complete reference for the SharpAPI v1 REST API. All endpoints return JSON and follow consistent conventions for authentication, filtering, pagination, and error handling.
Base URL
https://api.sharpapi.io/api/v1Always use api.sharpapi.io as the base URL, not sharpapi.io.
OpenAPI Specification
The full OpenAPI 3.1 spec is available for import into tools like Postman, Insomnia, or code generators:
This spec covers all endpoints, request/response schemas, and authentication requirements.
Request Format
- All requests use HTTPS
- Authentication via
X-API-Keyheader,Authorization: Bearerheader, orapi_keyquery param - Request bodies (where applicable) use JSON with
Content-Type: application/json - All field names are
snake_case
Endpoint Summary
SharpAPI exposes 35 routes across 7 namespaces plus reference and health endpoints.
Odds
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/odds | Yes | Free |
GET | /api/v1/odds/delta | Yes | Free |
GET | /api/v1/odds/best | Yes | Free |
GET | /api/v1/odds/comparison | Yes | Free |
POST | /api/v1/odds/batch | Yes | Free |
Events
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/events | Yes | Free |
GET | /api/v1/events/:eventId | Yes | Free |
GET | /api/v1/events/:eventId/odds | Yes | Free |
GET | /api/v1/events/:eventId/markets | Yes | Free |
Opportunities
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/opportunities/ev | Yes | Pro |
GET | /api/v1/opportunities/arbitrage | Yes | Hobby |
GET | /api/v1/opportunities/middles | Yes | Pro |
Splits
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/splits | Yes | Pro |
GET | /api/v1/splits/history | Yes | Pro |
Streaming
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/stream | Yes | Add-on |
Account
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/account | Yes | Free |
GET | /api/v1/account/usage | Yes | Free |
GET | /api/v1/account/keys | Yes | Free |
POST | /api/v1/account/keys | Yes | Free |
DELETE | /api/v1/account/keys/:keyId | Yes | Free |
POST | /api/v1/account/keys/:keyId/rotate | Yes | Free |
Reference Data (Public)
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/sports | No | - |
GET | /api/v1/leagues | No | - |
GET | /api/v1/sportsbooks | No | - |
GET | /api/v1/markets | No | - |
GET | /api/v1/teams | No | - |
Enterprise
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/futures | Yes | Enterprise |
GET | /api/v1/futures/odds | Yes | Enterprise |
Health
| Method | Path | Auth | Min Tier |
|---|---|---|---|
GET | /api/v1/health | No | - |
Response Format
Success Envelope
All successful responses wrap data in a consistent envelope:
{
"success": true,
"data": [
{
"id": "draftkings_33483153_moneyline_PHO",
"sportsbook": "draftkings",
"sport": "basketball",
"home_team": "PHI 76ers",
"away_team": "PHO Suns",
"market_type": "moneyline",
"selection": "PHO Suns",
"odds_american": -150,
"odds_decimal": 1.667,
"odds_probability": 0.60,
"is_live": false
}
],
"pagination": {
"limit": 50,
"offset": 0,
"has_more": true,
"next_offset": 50,
"next_cursor": "eyJlIjoiMzM0ODMxNTMiLCJiIjoiZHJhZnRraW5ncyIsIm0iOiJtb25leWxpbmUiLCJpIjoiZHJhZnRraW5nc18zMzQ4MzE1M19tb25leWxpbmVfUEhJIn0"
},
"meta": {
"count": 1,
"total": 3095,
"pagination": {
"limit": 50,
"offset": 0,
"has_more": true,
"next_offset": 50,
"next_cursor": "eyJlIjoiMzM0ODMxNTMiLCJiIjoiZHJhZnRraW5ncyIsIm0iOiJtb25leWxpbmUiLCJpIjoiZHJhZnRraW5nc18zMzQ4MzE1M19tb25leWxpbmVfUEhJIn0"
},
"updated_at": "2026-01-26T02:10:37.846Z",
"filters": {
"sportsbook": "draftkings",
"league": "nba"
}
}
}| Field | Description |
|---|---|
success | true for all successful responses |
data | Array of results (or single object for detail endpoints) |
pagination.limit | Maximum items per page (default: 50, max: 200) |
pagination.offset | Current offset into results |
pagination.has_more | Whether more results exist beyond this page |
pagination.next_offset | Offset value to use for the next page (legacy — use next_cursor for live-data endpoints) |
pagination.next_cursor | Opaque cursor for the next page. Pass as ?cursor= — stable against live data changes. Recommended for multi-page scans. |
meta.count | Number of items returned in this response |
meta.total | Total matching items across all pages |
meta.pagination | Same as top-level pagination (included for backward compatibility) |
meta.updated_at | ISO 8601 timestamp of when the data was last refreshed |
meta.filters | Echo of the filters applied to this request |
Single Resource Response
{
"success": true,
"data": {
"id": "evt_abc123",
"sport": "nba",
"home_team": "PHI 76ers",
"away_team": "PHO Suns",
"start_time": "2026-01-26T19:00:00Z"
},
"meta": {
"updated_at": "2026-01-26T02:10:37.846Z"
}
}Error Envelope
All errors follow a consistent format:
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Upgrade your plan or wait 45 seconds.",
"docs": "https://docs.sharpapi.io/en/authentication#rate-limits"
}
}| Field | Description |
|---|---|
error.code | Machine-readable error code |
error.message | Human-readable description |
error.docs | Link to relevant documentation (optional — included on auth, tier, rate limit, and streaming errors) |
error.correct_endpoint | The correct endpoint to use (optional — included on unknown_endpoint errors when the API recognizes the path as a known wrong route) |
error.retry_after | Seconds until retry is allowed (optional — included on rate_limited errors) |
Standard Response Headers
Every API response includes these headers:
| Header | Example | Description |
|---|---|---|
X-RateLimit-Limit | 300 | Maximum requests per minute for your tier |
X-RateLimit-Remaining | 299 | Requests remaining in the current window |
X-RateLimit-Reset | 1705804800 | Unix timestamp when the rate limit resets |
X-Data-Delay | 0 | Data delay in seconds (60 for Free, 0 for paid tiers) |
X-Request-Id | req_a1b2c3d4 | Unique request identifier for debugging and support |
Include the X-Request-Id value when contacting support about a specific request.
Tier Comparison
| Feature | Free | Hobby | Pro | Sharp | Enterprise |
|---|---|---|---|---|---|
| Sportsbooks | 2 (DK, FD) | 5 | 15 | All 32 | All 32 |
| Data Delay | 60s | 0s | 0s | 0s | 0s |
| Requests/min | 12 | 120 | 300 | 1,000 | Custom |
| Pinnacle | No | No | Yes | Yes | Yes |
| EV / Arb / Middles | No | No | Yes | Yes | Yes |
| Streaming | No | Add-on | Add-on | Add-on | Included |
| Batch max events | 10 | 10 | 50 | 50 | 100 |
Streaming requires the WebSocket add-on ($99/mo) on paid tiers. Enterprise includes streaming at no extra cost.
Filtering
All list endpoints support filtering via query parameters. Filters use singular parameter names and accept comma-separated values for multi-select.
Filter Parameters
| Parameter | Type | Example | Description |
|---|---|---|---|
sportsbook | string | draftkings,fanduel | Filter by one or more sportsbooks |
sport | string | basketball,football | Filter by sport |
league | string | nba,nfl | Filter by league |
market | string | moneyline,spread | Filter by market type |
event | string | evt_abc123 | Filter by event ID |
live | boolean | true | Only live/in-progress events |
sort | string | -odds_american | Sort field with optional - prefix for descending |
fields | string | id,sportsbook,odds_american | Comma-separated fields to include in response |
min_odds | number | -110 | Minimum American odds filter |
max_odds | number | 200 | Maximum American odds filter |
group_by | string | event | Group results by field |
limit | number | 25 | Results per page (default: 50, max: 200) |
offset | number | 50 | Pagination offset. May produce duplicate rows on live endpoints when data changes between requests. |
cursor | string | — | Opaque cursor from next_cursor. Recommended for multi-page scans over live data — eliminates offset drift. |
Examples
# Multiple sportsbooks (comma-separated)
curl "https://api.sharpapi.io/api/v1/odds?sportsbook=draftkings,fanduel" \
-H "X-API-Key: YOUR_KEY"
# Combine filters
curl "https://api.sharpapi.io/api/v1/odds?league=nba,nhl&sportsbook=pinnacle&live=true" \
-H "X-API-Key: YOUR_KEY"
# Paginate through results
curl "https://api.sharpapi.io/api/v1/odds?limit=25&offset=50" \
-H "X-API-Key: YOUR_KEY"Parameter names are always singular (sportsbook, not sportsbooks). Use commas to pass multiple values: sportsbook=draftkings,fanduel.
Odds Formats
All odds are returned as flat fields on each odds object:
{
"odds_american": -110,
"odds_decimal": 1.909,
"odds_probability": 0.5238
}| Field | Example | Description |
|---|---|---|
odds_american | -110, +150 | Standard US format |
odds_decimal | 1.909, 2.50 | Multiplier format |
odds_probability | 0.5238 | Implied probability (0 to 1) |
Error Codes
The canonical list of error codes emitted by the API. The string in error.code is the stable contract — check it rather than the prose message.
HTTP error codes
Emitted in REST responses.
| Code | HTTP Status | Description |
|---|---|---|
missing_api_key | 401 | No API key supplied via X-API-Key, ?api_key=, or Authorization: Bearer |
invalid_api_key | 401 | API key was supplied but is not recognized |
expired_api_key | 401 | API key has expired — generate a new one in the dashboard |
disabled_api_key | 401 | API key is disabled, usually because the subscription lapsed |
unauthorized | 401 | Bearer-token auth failed on admin or /api/v1/monitoring/* endpoints (distinct from invalid_api_key) |
invalid_token | 401 | Clerk session token failed verification (dashboard-only endpoints) |
tier_restricted | 403 | Endpoint or feature is not available on your tier — response includes tier and required_tier |
not_found | 404 | Resource not found |
method_not_allowed | 405 | HTTP method is not supported on this route |
gone | 410 | Event has ended or expired — use /api/v1/events for current events |
unknown_endpoint | 410 | Wrong API path — response includes correct_endpoint with the right route (e.g. /api/v1/arbitrage → /api/v1/opportunities/arbitrage) |
validation_error | 400 | Request body or query parameters failed validation |
offset_too_large | 400 | offset exceeds the per-endpoint maximum (currently 500 on /odds and /odds/delta). Switch to cursor-based pagination or advance since |
rate_limited | 429 | Per-minute request rate limit exceeded — response includes retry_after |
concurrent_request_cap | 429 | Too many in-flight requests for your tier — reduce parallelism or upgrade |
too_many_streams | 429 | Maximum concurrent SSE or WebSocket streams exceeded for this API key |
backpressure | — | Emitted as an SSE event: error (not an HTTP status) when the client cannot keep up; server then closes the stream |
upstream_error | 502 | Temporary issue reaching an upstream sportsbook or auth provider |
service_unavailable | 503 | A required service (e.g. key management) is not configured or is offline |
not_ready | 503 | A backing store the endpoint depends on (e.g. ClickHouse, closing-line capture) is not yet ready to serve the request. Retry with backoff |
internal_error | 500 | Unexpected server error — retry with backoff and contact support with X-Request-Id if it persists |
bad_request and invalid_request are deprecated — both were collapsed into validation_error. Clients that match on the old strings should add validation_error to their error map.
WebSocket frame error codes
Emitted only inside {"type": "error", "code": "..."} frames on an open WebSocket connection. They describe client-protocol mistakes and do not correspond to an HTTP status.
| Code | Meaning |
|---|---|
invalid_message | Frame could not be parsed as JSON or did not match the expected shape |
unknown_message_type | type field is not one of the documented values (auth, subscribe, filter, refresh_token, ping) |
missing_token | auth or refresh_token frame did not include a token field |
missing_channels | subscribe frame did not include a non-empty channels array |
not_authenticated | Frame requires an authenticated session (sent subscribe, filter, or refresh_token before auth) |
already_authenticated | Client sent a second auth frame after the first one succeeded |