Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/sharpapi:sharpapi-game-state-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Sharp Game State API
version: 3.28.0
description: Real-time sports betting odds API with +EV detection, arbitrage, middles, and low-hold opportunities.
contact:
name: SharpAPI Support
url: https://sharpapi.io
email: support@sharpapi.io
license:
name: Proprietary
url: https://sharpapi.io/terms
x-generated-at: '2026-10-13T20:02:34Z'
x-commit-sha: 850f5cb
servers:
- url: https://api.sharpapi.io/api/v1
description: Production
security:
- ApiKeyHeader: []
- BearerAuth: []
tags:
- name: Game State
description: Live game state data (Game State add-on or Enterprise)
paths:
/gamestate:
get:
operationId: listAllGameState
summary: Live game state for every sport
description: 'Returns aggregated live game state (scores, periods, clocks, situational data) for every sport with active events. Merged across sportsbooks with a consensus-based algorithm so a single misbehaving book can''t skew the result. Requires the **Game State add-on** ($79/mo, available on any paid plan) or the **Enterprise** tier. A fixture that has just finished stays here for 15 minutes after completion as a terminal row (`status: "final"`) — see the `status` / `is_live` parameters and the `EventGameState` schema.'
tags:
- Game State
responses:
'200':
description: Live game state keyed by sport then event_id
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DataEnvelope'
- type: object
properties:
data:
type: object
description: Map of sport name to a map of event_id → EventGameState
additionalProperties:
type: object
additionalProperties:
$ref: '#/components/schemas/EventGameState'
example:
data:
soccer:
argentina_-_primera_division_bocajuniors_defensayjusticia_2026-04-23:
home_team: Defensa y Justicia
away_team: Boca Juniors
sport: soccer
league: argentina_-_primera_division
home_score: 0
away_score: 1
game_period: 2H
game_clock: '49:06'
is_live: true
possession: away
corners_home: 1
corners_away: 0
primary_book: draftkings
book_count: 6
montenegro_-_prva_liga_bokelj_otrantolympiculcinj_2026-04-23:
home_team: Bokelj
away_team: FK Otrant-Olympic Ulcinj
sport: soccer
league: montenegro_-_prva_liga
home_score: 2
away_score: 1
score_type: points
winner: home
status: final
completed_at: '2026-04-23T23:48:55Z'
is_live: false
stale: false
baseball:
mlb_redsox_yankees_2026-04-23:
home_team: New York Yankees
away_team: Boston Red Sox
sport: baseball
league: mlb
home_score: 2
away_score: 1
game_period: T5
is_live: true
primary_book: fliff
book_count: 9
updated_at: '2026-04-23T23:55:01.234Z'
'400':
description: Unrecognised `status` or `is_live` value. The error body names the accepted set in `details.accepted`.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: validation_error
message: 'is_live must be one of: true, false'
details:
parameter: is_live
accepted:
- 'true'
- 'false'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/TierRestricted'
parameters:
- name: status
in: query
required: false
schema:
type: string
enum:
- final
example: final
description: Return only the carried terminal (finished) rows. `final` is the only accepted value — live rows are the default set, and `is_live=true` selects them alone. Case-insensitive. Any other value returns `400 validation_error` with `details.accepted` = `["final"]`.
- name: is_live
in: query
required: false
schema:
type: string
enum:
- 'true'
- 'false'
example: 'true'
description: '`true` returns live rows only, excluding every carried terminal row; `false` returns non-live rows only, which today means the carried terminal rows. Case-insensitive. Any other value — including `1` and `0` — returns `400 validation_error` with `details.accepted` = `["true","false"]`.'
/gamestate/{sport}:
get:
operationId: getGameStateForSport
summary: Live game state for a single sport
description: 'Returns aggregated live game state for events in one sport. Response shape matches `GET /gamestate` but `data` contains a single key (the path `sport`). Unknown sports return an empty `data` object, never 404. Requires the **Game State add-on** ($79/mo, available on any paid plan) or the **Enterprise** tier. A fixture that has just finished stays here for 15 minutes after completion as a terminal row (`status: "final"`) — see the `status` / `is_live` parameters and the `EventGameState` schema.'
tags:
- Game State
parameters:
- name: sport
in: path
required: true
schema:
type: string
example: soccer
description: 'Atlas sport id. Case-insensitive. Known values: `baseball`, `basketball`, `football`, `hockey`, `soccer`, `tennis`, `table_tennis`, `cricket`, `esports`, `snooker`, `other`.'
- name: status
in: query
required: false
schema:
type: string
enum:
- final
example: final
description: Return only the carried terminal (finished) rows. `final` is the only accepted value — live rows are the default set, and `is_live=true` selects them alone. Case-insensitive. Any other value returns `400 validation_error` with `details.accepted` = `["final"]`.
- name: is_live
in: query
required: false
schema:
type: string
enum:
- 'true'
- 'false'
example: 'true'
description: '`true` returns live rows only, excluding every carried terminal row; `false` returns non-live rows only, which today means the carried terminal rows. Case-insensitive. Any other value — including `1` and `0` — returns `400 validation_error` with `details.accepted` = `["true","false"]`.'
responses:
'200':
description: Live game state for the requested sport (envelope identical to `/gamestate`, just scoped to one sport)
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/DataEnvelope'
- type: object
properties:
data:
type: object
description: Map with a single key (the path sport) whose value is a map of event_id → EventGameState.
additionalProperties:
type: object
additionalProperties:
$ref: '#/components/schemas/EventGameState'
example:
data:
soccer:
chile_-_primera_division_concepcion_palestino_2026-04-24:
home_team: Palestino
away_team: Concepción
sport: soccer
league: chile_-_primera_division
home_score: 0
away_score: 0
is_live: true
primary_book: unibet
book_count: 1
stale: true
montenegro_-_prva_liga_bokelj_otrantolympiculcinj_2026-04-23:
home_team: Bokelj
away_team: FK Otrant-Olympic Ulcinj
sport: soccer
league: montenegro_-_prva_liga
home_score: 2
away_score: 1
score_type: points
winner: home
status: final
completed_at: '2026-04-23T23:48:55Z'
is_live: false
stale: false
updated_at: '2026-04-23T23:55:01.234Z'
'400':
description: Unrecognised `status` or `is_live` value. The error body names the accepted set in `details.accepted`.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: validation_error
message: 'is_live must be one of: true, false'
details:
parameter: is_live
accepted:
- 'true'
- 'false'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/TierRestricted'
components:
schemas:
DataEnvelope:
type: object
description: Standard shape for non-paginated responses. Emitted by endpoints that return a single resource or a small fixed list. No `success` flag — HTTP status codes indicate success/failure.
required:
- data
- updated_at
properties:
data: {}
updated_at:
type: string
format: date-time
description: Server emit time (RFC 3339).
ErrorResponse:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
enum:
- missing_api_key
- invalid_api_key
- unauthorized
- forbidden
- tier_restricted
- rate_limited
- invalid_request
- validation_error
- not_found
- streaming_not_available
- stream_limit_exceeded
- service_unavailable
- internal_error
description: Machine-readable error code
message:
type: string
description: Human-readable error message
docs:
type: string
format: uri
description: Link to relevant documentation
tier:
type: string
description: Current subscription tier
required_tier:
type: string
description: Minimum tier required for this feature
retry_after:
type: integer
description: Unix timestamp (ms) when request can be retried
retryAfter:
type: integer
description: Seconds until the request can be retried — same instant as retry_after, different unit and casing
max_books:
type: integer
requested_books:
type: integer
EventGameState:
type: object
description: 'Aggregated live game state for a single event, merged across the sportsbooks covering it. Scores are consensus-selected; temporal fields use sport-aware clock direction; situational fields priority-fill from a fixed book ranking. Every field is optional except the always-present core set — sport coverage varies, so e.g. tennis rows carry sets_home/server while hockey rows carry power_play. Rows also come in a terminal flavour: a fixture that has finished is carried here for 15 minutes after completion with status "final", completed_at, score_type and the final scores. A terminal row is projected from the completion record, not from a live cross-book merge, so it carries no primary_book/book_count/game_period/game_clock and no situational fields — branch on status == "final" before reading those.'
required:
- home_team
- away_team
- sport
- league
- is_live
properties:
home_team:
type: string
description: Normalized home team name.
away_team:
type: string
description: Normalized away team name.
sport:
type: string
description: Atlas sport id (e.g. `baseball`, `soccer`).
league:
type: string
description: Atlas league id (e.g. `mlb`, `england_-_premier_league`).
home_score:
type: integer
description: 'Current home score in the natural unit for the sport (runs, points, goals, sets — tennis uses summed set points). Present on live rows and on terminal rows whose `score_type` is `points`; a terminal row with `score_type: "sets"` carries `sets_home`/`sets_away` instead and omits this.'
away_score:
type: integer
description: 'Current away score. Present on live rows and on terminal rows whose `score_type` is `points`; a terminal row with `score_type: "sets"` carries `sets_home`/`sets_away` instead and omits this.'
is_live:
type: boolean
description: '`true` for a live event; `false` on a carried terminal (finished) row.'
primary_book:
type: string
description: The sportsbook whose scores were selected by the merge. Consensus-respecting — if ≥2 books agree on a score the highest-priority book in that group wins. Live rows only — absent on a terminal row.
book_count:
type: integer
description: Number of sportsbooks that contributed live state for this event at merge time. Live rows only — absent on a terminal row.
game_period:
type: string
description: 'Current period. Sport-specific format: `T5` (MLB top 5), `Q3` (NBA Q3), `2H` (soccer 2nd half), `P2` (NHL P2), `S2` (tennis set 2), `FT` (full time).'
game_clock:
type: string
description: Game clock. Countup format (`49:06`) for soccer; countdown format (`5:42`) for basketball/hockey/football.
possession:
type: string
enum:
- home
- away
description: Which team has possession (team sports) or is receiving (tennis).
server:
type: string
enum:
- home
- away
description: Tennis only — current server.
last_play:
type: string
description: Sport-specific last-play description.
is_timeout:
type: boolean
description: '`true` during a timeout.'
fouls_home:
type: integer
description: Team fouls (basketball, soccer).
fouls_away:
type: integer
corners_home:
type: integer
description: Soccer only.
corners_away:
type: integer
yellow_cards_home:
type: integer
description: Soccer only.
yellow_cards_away:
type: integer
red_cards_home:
type: integer
description: Soccer only.
red_cards_away:
type: integer
power_play:
type: string
enum:
- home
- away
description: Hockey only — which team is on the power play.
sets_home:
type: integer
description: Tennis only — sets won.
sets_away:
type: integer
hits_home:
type: integer
description: Baseball only.
hits_away:
type: integer
home_pitcher:
type: string
description: Baseball only — starting pitcher display name.
away_pitcher:
type: string
wickets_home:
type: integer
description: Cricket only — dismissals.
wickets_away:
type: integer
overs:
type: number
description: Cricket only.
batting_team:
type: string
enum:
- home
- away
description: Cricket only.
stale:
type: boolean
description: Only emitted when `true`. Indicates the `primary_book`'s livestate key TTL fell below the aggregator's freshness threshold (~10s) at merge time — scores reflect the last known values; the book has gone quiet. Absence of the field means fresh. On live rows it is written only when `true` (absence = fresh); a terminal row always carries it explicitly as `false`.
aggregator_stale:
type: boolean
description: Only emitted when `true`. Indicates the SharpAPI aggregator itself hasn't advanced the gen counter for this event's sport in more than ~30s. Broader signal than `stale` — suggests a pipeline hiccup, not a single-book issue.
status:
type: string
enum:
- final
description: Present only on a carried terminal row, always `final`. Live rows carry no `status` key, so `status == "final"` is the reliable terminal test.
completed_at:
type: string
format: date-time
description: Terminal rows only — when SharpAPI detected the completion (not a league-official final whistle). The 15-minute carry window is measured from this timestamp.
score_type:
type: string
enum:
- points
- sets
description: Terminal rows only — which fields carry the final result. `sets` for the set-scored sports (`tennis`, `table_tennis`, `volleyball`, `badminton`), whose result is in `sets_home`/`sets_away`; `points` everywhere else, whose result is in `home_score`/`away_score`.
winner:
type: string
enum:
- home
- away
- draw
description: 'Terminal rows only, and optional even there — omitted when the recorded result does not settle the match (a level score in a sport that cannot draw, a sub-terminal set count, or a snapshot whose scores never arrived and is recorded as 0-0). A missing `winner` means "not settled", not a tie. `draw` is produced only for sports where a level score is a real outcome: `soccer`, `hockey`, `football`, `rugby_union`, `rugby_league`.'
responses:
Unauthorized:
description: Missing or invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
missing:
summary: Missing API key
value:
error:
code: missing_api_key
message: API key required
docs: https://docs.sharpapi.io/en/authentication#authentication-methods
invalid:
summary: Invalid API key
value:
error:
code: invalid_api_key
message: Invalid API key
docs: https://docs.sharpapi.io/en/authentication#authentication-methods
TierRestricted:
description: Feature requires a higher subscription tier
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: tier_restricted
message: EV opportunities requires pro tier or higher
tier: hobby
required_tier: pro
docs: https://sharpapi.io/pricing
securitySchemes:
ApiKeyHeader:
type: apiKey
in: header
name: X-API-Key
description: API key passed in the `X-API-Key` header
BearerAuth:
type: http
scheme: bearer
description: API key passed as a Bearer token in the `Authorization` header
ApiKeyQuery:
type: apiKey
in: query
name: api_key
description: API key as query parameter (for SSE/EventSource only)