Endpoint API Reference
This page documents the customer-facing route for fetching data from a Spartera Endpoint. This is the route buyers (or their apps and agents) call after subscribing.
Base URL
Endpoints are served from a per-seller subdomain:
https://{seller_handle}.api.spartera.com
The {seller_handle} is the subdomain identifier for the company that owns the endpoint. For example, an endpoint owned by txodds lives at https://txodds.api.spartera.com.
In stage:
https://{seller_handle}.stage.api.spartera.com
Fetching Data
Endpoint
GET /endpoints/v1/{endpoint_slug}
The slug is human-readable and unique within the seller's company (e.g., nfl-passing-yards-by-team).
Headers
| Header | Required | Description |
|---|---|---|
x-api-key | ✅ | An endpoint-type API key (ep_...) scoped to this endpoint |
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
start | 0 | Pagination cursor — row offset | |
limit | seller's cap | Number of rows to return (capped by max_records_per_request) | |
| Any column name or alias from the endpoint schema | Dynamic filter (e.g., ?team=ARI) |
Dynamic filters support operator prefixes: ?yards=>=100 (greater-than-or-equal), ?team=!=BUF (not-equal). Supported operators: =, !=, <, >, <=, >=.
Example Request
curl "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?start=0&limit=100" \
-H "x-api-key: ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi"Response Envelope
Every successful response has the same three-block shape:
{
"message": "success",
"data": {
"spartera": {
"request_id": "d23e815b-a490-495a-8b07-4eee717f2d08",
"generated_at": "2026-04-28T20:37:25.493556+00:00",
"billed": true,
"bill_trigger": "200_OK",
"credits_consumed": 2,
"page": 1,
"page_size": 100,
"total_returned": 37,
"has_more": false,
"next_start": null,
"next_page": null
},
"request": {
"endpoint": "nfl-passing-yards-by-team",
"endpoint_id": "a72b8513-a6e8-4b38-9982-3ccd0d64339a",
"limit": 100,
"params": {},
"source": "nflverse.com",
"start": 0
},
"response": {
"data": [
{ "team": "ARI", "passing_yards": 4150 },
{ "team": "BUF", "passing_yards": 4892 }
]
}
}
}spartera block
spartera blockRequest metadata and billing info.
| Field | Description |
|---|---|
request_id | Unique ID for this request — useful for support tickets |
generated_at | UTC timestamp when the response was generated |
billed | true if credits were deducted, false if seller-owned |
bill_trigger | When billing happened — currently always 200_OK |
credits_consumed | Credits deducted for this request (0 for free or seller-owned) |
page, page_size, total_returned | Current page info |
has_more | true if there are more rows beyond this page |
next_start, next_page | Cursors for the next page (null if has_more is false) |
request block
request blockEcho of the request — what was actually queried.
response.data block
response.data blockThe rows. Always an array. Empty array if no rows match.
Pagination
Pagination uses start and limit query parameters. To paginate through results:
import requests
URL = "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team"
HEADERS = {"x-api-key": "ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi"}
start = 0
limit = 100
while True:
response = requests.get(URL, headers=HEADERS, params={"start": start, "limit": limit})
body = response.json()["data"]
for row in body["response"]["data"]:
print(row)
if not body["spartera"]["has_more"]:
break
start = body["spartera"]["next_start"]The seller sets max_records_per_request per endpoint (default 1,000). Requesting limit=10000 against an endpoint with a cap of 1,000 will silently return at most 1,000 rows.
Filtering
Pass any non-hidden column from the endpoint's schema as a query parameter to filter rows server-side:
# Equality
curl "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?team=ARI" \
-H "x-api-key: ep_..."
# Greater-than
curl "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?passing_yards=>4000" \
-H "x-api-key: ep_..."
# Combined with pagination
curl "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?team=BUF&start=0&limit=50" \
-H "x-api-key: ep_..."Operators: = (default), !=, <, >, <=, >=. Use the operator inline with the value.
Buyer-supplied filters are translated into SQL WHERE conditions and combined with any seller-configured filters using AND.
Code Examples
Python
import requests
response = requests.get(
"https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team",
headers={"x-api-key": "ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi"},
params={"start": 0, "limit": 100}
)
body = response.json()
print(f"{body['data']['spartera']['total_returned']} rows returned")
for row in body["data"]["response"]["data"]:
print(row)JavaScript
const response = await fetch(
"https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?start=0&limit=100",
{
method: "GET",
headers: { "x-api-key": "ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi" }
}
);
const { data } = await response.json();
console.log(`${data.spartera.total_returned} rows returned`);
data.response.data.forEach(row => console.log(row));Go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET",
"https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?start=0&limit=100",
nil)
req.Header.Set("x-api-key", "ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
fmt.Println(result)
}cURL
curl "https://txodds.api.spartera.com/endpoints/v1/nfl-passing-yards-by-team?start=0&limit=100" \
-H "x-api-key: ep_HeRGNo0qyvi64sRVAZI3I0GW77LpCYWi"HTTP Status Codes
| Code | Meaning |
|---|---|
200 OK | Success — data returned, credits deducted (if applicable) |
400 Bad Request | Invalid parameters or subdomain mismatch |
401 Unauthorized | Missing or invalid API key |
403 Forbidden | Key valid but not permitted (e.g., IP not whitelisted, or key scoped to a different endpoint) |
404 Not Found | Endpoint slug doesn't exist (or company subdomain doesn't match the endpoint's company) |
429 Too Many Requests | Seller-configured rate limit exceeded |
502 Bad Gateway | Seller-side error (database unreachable, query timeout) |
503 Service Unavailable | Endpoint is not in ACTIVE status |
500 Internal Server Error | Spartera platform error |
Error Responses
Errors return a structured body with message and data.error_type:
{
"message": "Insufficient credits. Required: 2, Available: 0",
"data": {
"error_type": "INSUFFICIENT_CREDITS",
"error_source": "BUYER",
"request_id": "d23e815b-a490-495a-8b07-4eee717f2d08",
"endpoint_id": "a72b8513-a6e8-4b38-9982-3ccd0d64339a"
}
}error_source | Who's responsible |
|---|---|
BUYER | The caller (insufficient credits, bad params, IP not whitelisted) |
SELLER | The data provider (database unreachable, query failed) |
SPARTERA | The platform |
You are never charged credits for non-200 responses. If a request fails for any reason — bad input, seller-side database error, platform error — no credits are deducted.
Rate Limiting
Sellers can configure per-buyer rate limits on their endpoints. When a limit is exceeded, you'll receive a 429 with details:
{
"error": "Rate limit exceeded",
"message": "Endpoint allows 100 requests per minute at USER level",
"rate_limit": {
"limit": 100,
"period": "minute",
"granularity": "USER",
"remaining": 0,
"reset_in_seconds": 47
},
"retry_after": 47
}Granularity options: IP, USER, COMPANY, API_KEY, GLOBAL.
Related Pages
- API Keys — How endpoint keys work and where to get one
- Endpoints Overview — What an endpoint is
- Subscribing to Endpoints — How buyers get keys
- Credit System — How billing works
