Kappa Data APIDocumentation
Public docs

TradePolaris Data API

Search versioned datasets, read each schema, query bounded rows and export an immutable Parquet version. Pin the version, and the same request returns the same result later.

The reference is public, but calling the API needs an approved Kappa account and an active key.

Base URL
https://api.tradepolaris.com/platform/v1
Authentication
Bearer API key
Access model
Read-only datasets
Contract
1.0.0-beta.1 · beta
Response formats
JSON, CSV, Parquet
Start here

Make your first request

Search for a dataset and inspect its schema before choosing filters, so you never guess slugs, columns, date fields or point-in-time support.

  1. 1
    Create a key

    Approved accounts create keys in the authenticated Data API console. Store the show-once secret in a secrets manager.

  2. 2
    Search, then inspect

    Resolve a dataset with GET /datasets?q=…, then read /schema before querying rows.

  3. 3
    Query or export

    Use bounded JSON for applications, complete-or-rejected CSV for filtered extracts, or checksummed Parquet for a full immutable version.

curlcopy-ready
export TP_API_KEY="tp_live_..."

curl --fail-with-body \
  --header "Authorization: Bearer $TP_API_KEY" \
  "https://api.tradepolaris.com/platform/v1/datasets?q=apple&limit=10"
Python · requestscopy-ready
import hashlib
import os
import requests

BASE_URL = "https://api.tradepolaris.com/platform/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['TP_API_KEY']}"

# Search first; do not page the full catalog.
catalog = session.get(
    f"{BASE_URL}/datasets",
    params={"q": "apple", "limit": 10},
)
catalog.raise_for_status()
dataset = catalog.json()[0]["name"]

# Inspect capabilities before choosing filters.
schema_response = session.get(f"{BASE_URL}/datasets/{dataset}/schema")
schema_response.raise_for_status()
schema = schema_response.json()

# JSON rows are paginated and DataFrame-ready.
rows_response = session.get(
    f"{BASE_URL}/datasets/{dataset}/query",
    params={"start": "2026-01-01", "limit": 5000},
)
rows_response.raise_for_status()
page = rows_response.json()
print(page["columns"], page["rows"], page["truncated"])

# For the immutable full version, verify the exported Parquet bytes.
export_response = session.get(f"{BASE_URL}/datasets/{dataset}/export")
export_response.raise_for_status()
artifact = export_response.json()
download = requests.get(artifact["url"])  # no API Authorization header
download.raise_for_status()
expected_sha256 = artifact.get("content_sha256")
if not expected_sha256:
    raise RuntimeError("Export did not include a checksum")
assert hashlib.sha256(download.content).hexdigest() == expected_sha256
TypeScript · Node.jscopy-ready
import { createHash } from "node:crypto";

const BASE_URL = "https://api.tradepolaris.com/platform/v1";
const API_KEY = process.env.TP_API_KEY;
if (!API_KEY) throw new Error("Set TP_API_KEY in the server environment");

const sleep = (milliseconds: number) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));

async function api(path: string): Promise<Response> {
  const url = new URL(path, BASE_URL + "/");
  if (url.origin !== new URL(BASE_URL).origin) {
    throw new Error("Refusing to forward API auth to another origin");
  }
  for (let attempt = 0; attempt < 5; attempt += 1) {
    const response = await fetch(url, {
      headers: { Authorization: "Bearer " + API_KEY },
    });
    if (![429, 503].includes(response.status) || attempt === 4) return response;
    const seconds = Number(response.headers.get("Retry-After"));
    const delay = Number.isFinite(seconds) && seconds > 0
      ? Math.min(seconds * 1000, 5000)
      : Math.min(250 * 2 ** attempt, 5000);
    await sleep(delay);
  }
  throw new Error("unreachable");
}

async function json(path: string) {
  const response = await api(path);
  if (!response.ok) throw new Error("Data API returned " + response.status);
  return response.json();
}

const catalog = await json("datasets?q=example&limit=10");
if (!catalog[0]) throw new Error("No matching dataset");
const name = catalog[0].name;
const version = catalog[0].latest_version; // pin every following request
const route = "datasets/" + encodeURIComponent(name);
const schema = await json(route + "/schema?version=" + version);

let offset = 0;
const rows: unknown[][] = [];
for (let pageNumber = 0; pageNumber < 20; pageNumber += 1) {
  const page = await json(
    route + "/query?version=" + version + "&limit=5000&offset=" + offset,
  );
  rows.push(...page.rows);
  if (!page.truncated) break;
  if (page.returned < 1 || pageNumber === 19) {
    throw new Error("Pagination did not terminate within the client bound");
  }
  offset += page.returned;
}

const artifact = await json(route + "/export?version=" + version);
const download = await fetch(artifact.url); // deliberately no Authorization header
if (!download.ok) throw new Error("Export download returned " + download.status);
const bytes = Buffer.from(await download.arrayBuffer());
const actual = createHash("sha256").update(bytes).digest("hex");
if (!artifact.content_sha256 || actual !== artifact.content_sha256) {
  throw new Error("Export checksum mismatch");
}
console.log({ columns: schema.columns.length, rows: rows.length, version });
Access

Authentication and scopes

Send the key on every request. The secret starts with tp_live_ and is shown only once.

HTTP headercopy-ready
Authorization: Bearer tp_live_...
Keep keys in trusted server-side clients.

Never put a Data API key in browser bundles, local storage, mobile binaries or client-side environment variables. Browser CORS is not supported, so call Kappa from a server you control.

Recommended

Curated scope

Reads the shared catalog and curated datasets. The account's own uploads stay hidden and return 404.

Internal only

All-datasets scope

Adds the account's own datasets, for trusted internal services that need those rows.

  • Keys expire after 1–365 days (90 by default).
  • Up to five keys can be active on one account.
  • Optional IPv4 and IPv6 CIDRs restrict where a key works (an empty list means unrestricted).
  • Rotation keeps the service-account identity, with an optional bounded overlap.
  • Revocation rejects the key immediately on every API endpoint.
Compatibility

A versioned contract with an explicit change trail

The current public wire contract is 1.0.0-beta.1 (beta), and CI fingerprints its endpoints, parameters, response fields, headers and error shapes against the latest changelog entry.

additive

A new optional field, parameter, operation, media type, or other backwards-compatible capability.

clarification

Documentation or examples become more precise without changing request or response behavior.

deprecated

An existing operation or field remains available but gains explicit replacement and announcement metadata.

breaking

A removal, rename, new requirement, narrowed input, or incompatible response change that requires a new contract version.

No operation is deprecated in this version.

A future deprecation will identify the affected operation, announcement date, and optional replacement. A sunset value and runtime Deprecation / Sunsetheaders will be published only after that behavior exists on the server.

Read the HTML changelog, JSON feed, or Markdown feed. The /health and /ready routes are operational diagnostics outside this five-operation contract and do not define supported integration behavior.

Reference

Dataset endpoints

Paths are relative to https://api.tradepolaris.com/platform/v1, and the read-only surface accepts no SQL or arbitrary ordering.

GET/datasets

Find a dataset

Search the datasets visible to the key. The catalog is large and alphabetically paged, so search by name or label instead of walking every page.

Parameters for GET /datasets
ParameterTypeMeaning
qstringSearch dataset names and human labels. For example, q=apple matches us-eq-aapl-1d.
asset_classenumOptional family filter: equity, intl, futures, fx, crypto, macro, fundamentals, options, positioning, settlement, news. An unrecognised value is ignored and the unfiltered catalog is returned.
limitinteger · 1–500default 200Maximum catalog rows in this page.
offsetinteger · ≥0default 0Zero-based catalog offset.

Response

body
An array of dataset summaries: name, kind, latest_version, source, license, description, owned, updated_at, and asset.
X-Has-More
The reliable pagination signal. Do not infer completion from the returned row count.
GET/datasets/{name}/preview

Preview its shape

Return a render-ready sample as line points, candles, or table rows. Use schema or query when a dataset is too large to preview.

Parameters for GET /datasets/{name}/preview
ParameterTypeMeaning
namerequiredstringDataset slug returned by GET /datasets.
versioninteger · ≥1Pinned dataset version. Omit to use the latest version.
max_pointsintegerdefault 500Requested preview density. Values are safely clamped to 10–2,000 points.

Response

chart
line, candles, or table; identifies the populated payload field.
points / candles / rows
The render-ready preview payload.
total_rows / downsampled
Full version size and whether the preview was thinned.
GET/datasets/{name}/schema

Inspect the schema

Read columns, dtypes, coverage, detected time and symbol fields, and the producer-declared capability contract before querying rows.

Parameters for GET /datasets/{name}/schema
ParameterTypeMeaning
namerequiredstringDataset slug returned by GET /datasets.
versioninteger · ≥1Pinned dataset version. Omit to use the latest version.

Response

columns[] / row_count
Column names and dtypes plus the version's total row count.
date_column / symbol_column
Detected effective-time and symbol fields when present.
date_min / date_max
Dataset- and version-specific coverage bounds.
capabilities
The source contract, including point_in_time_supported, time fields, revision_model, valid query operators, and provenance.
GET/datasets/{name}/query

Query rows

Apply structured filters and column projection. This is not a SQL endpoint. JSON is paginated; CSV is complete-or-rejected up to 1,000,000 matching rows.

Parameters for GET /datasets/{name}/query
ParameterTypeMeaning
namerequiredstringDataset slug returned by GET /datasets.
versioninteger · ≥1Pinned dataset version. Omit to use the latest version.
symbolstring · ≤32 charsExact symbol filter when the schema declares a symbol column.
startISO dateInclusive lower bound on the dataset's effective date field.
endISO dateInclusive upper bound on the dataset's effective date field.
as_ofYYYY-MM-DDPoint-in-time knowledge bound. Use only when schema.capabilities.point_in_time_supported is true; unsupported datasets return 400.
columnscomma-separated stringProjected column names.
limitinteger · 1–5,000default 500Maximum rows in a JSON page.
offsetinteger · ≥0default 0JSON page offset.
tailbooleandefault falseFor JSON, keep the newest bounded window instead of the oldest one. CSV is already the complete filtered result.
formatjson | csvdefault jsonJSON returns a bounded page. CSV returns the complete filtered result or rejects it before sending partial bytes.

Response

columns / rows
Column names and row arrays, ready to construct a DataFrame.
total_matched / returned / truncated
Exact match count and page status for JSON responses.
as_of evidence
When applied: as_of, knowledge_time_column, knowledge_time_min, and knowledge_time_max.
CSV headers
X-Total-Rows is the emitted row count and X-Export-Complete: true confirms the body was not clipped.
GET/datasets/{name}/export

Export a pinned version

Mint a short-lived URL for the immutable Parquet artifact. Download the URL without an Authorization header and verify its SHA-256 checksum.

Parameters for GET /datasets/{name}/export
ParameterTypeMeaning
namerequiredstringDataset slug returned by GET /datasets.
versioninteger · ≥1Pinned dataset version. Omit to use the latest version.
as_ofunsupportedDo not send this parameter. The endpoint returns 400 rather than silently exporting unfiltered history; use /query with as_of.

Response

url / expires_in
A bearer download URL and its lifetime in seconds: 900 for curated datasets and 300 for account-owned uploads.
name / version / bytes / rows
Immutable artifact identity and size.
content_sha256
Checksum to verify after download.
Completeness

Pagination without silent truncation

Catalog

Increase offset while the X-Has-More header is true, even after a short page.

JSON rows

Increase offset while truncated is true, and read total_matched for the exact count.

Newest window

Set tail=true for the newest bounded window, without arbitrary sorting.

CSV

A successful CSV is complete with X-Export-Complete: true, and oversized results fail before any body is sent.

Historical truth

Point-in-time queries are capability-based

Use as_of=YYYY-MM-DD only when the resolved schema says capabilities.point_in_time_supported: true. The bound means “known by the end of that UTC date” on the declared availability field. Unsupported datasets return 400 instead of silently ignoring the bound.

Do not infer support from a dataset name or column.

Read time_field, availability_time_field, revision_model, and the response's knowledge_time_min / knowledge_time_max. An empty result before the retained floor is not evidence that no filing or disclosure existed.

Dataset families with point-in-time support
Catalog selectorAvailabilityEffective timeRevision model
us-13f-hist-*filing_dateperiodfiling_history_with_amendments
us-form4-*filing_datetransaction_datefiling_history_with_amendments
house-ptr-*filed_datetransaction_daterolling_disclosure_history

Coverage is dataset- and version-specific, and Kappa makes no catalog-wide history promise, so inspect date_min and date_max. See the complete machine-readable capability matrix.

SEC 13F integration notes

History datasets keep every accession, including amendments, and are not amendment-resolved canonical portfolios. ADD-type amendments and firms that file through multiple entities require consumer-side interpretation. Do not infer a complete manager position unless an amendment-resolved portfolio is supplied.

Put, call, and unflagged positions remain distinct history rows. Use value_usd for normalized values, because value keeps the raw SEC number, whose unit changes across filing eras.

pct_of_filing_value is a weight within one accession. The legacy pct_of_portfolio field is a deprecated alias, not a canonical firm-level portfolio weight.

US equity reference data includes active and inactive/delisted symbols where supplied. That reduces survivorship bias; it does not certify any dataset or any reconstructed universe as complete. The us-listing-status lane records dated listing and delisting events for those symbols, so membership on a date can be rebuilt from the events effective on or before it, down to a 2005-01-01 floor. It is not index membership, it carries no delisting reason, and listing dates below that floor are not recoverable from it. Its membership is inclusive of non-common security types. See that lane's row for the security_class column that separates them.

Bulk data

Handle exports as short-lived bearer artifacts

  • Download the returned URL with a plain GET. Do not attach the TradePolaris API key to the object-storage request.
  • Verify content_sha256 before accepting the file. Versions are immutable, so a pinned re-download should be byte-identical.
  • Do not log, share or persist the URL (its response is Cache-Control: no-store). Anyone holding it can download until it expires.
  • Revoking the API key does not invalidate issued URLs, which stay valid until their TTL ends.
  • The export endpoint does not support as_of. Use a point-in-time CSV query when the filtered result fits the documented row ceiling.
Operations

Errors and retries

The default normal-capacity budget is 120 requests per minute per key and can vary by plan. On 429, wait for Retry-After. Retry transient 503 responses with capped exponential backoff.

400
The request asks for unsupported semantics, such as as_of on a dataset that does not declare point-in-time support.
401
The bearer key is missing, malformed, expired, revoked, or unknown.
402 dataset_limit
The monthly distinct-dataset export meter is exhausted.
403
The account is not entitled to Data API access, or the key's source-CIDR policy denies the trusted client address.
404
The dataset does not exist or is outside this key's scope. Private datasets are concealed rather than disclosed with 403.
413
The preview is too large, or a CSV query matches more than 1,000,000 rows. Narrow the query or use Parquet export.
422
One or more parameters failed validation.
429
The per-key request budget is spent. Wait for Retry-After, then retry.
503
A transient fail-closed service guard rejected the request. Retry with backoff.
Account API

Key-management routes use account authentication

A Data API key cannot manage keys. These routes need a TradePolaris account session or account JWT, and ownership comes from that identity, never from the request body.

POST /api-keys
Create a key and return its secret once.
GET /api-keys
List credential history and metadata, never secrets.
POST /api-keys/{id}/rotate
Create a same-principal successor with a bounded overlap.
PATCH /api-keys/{id}
Update the label or source-CIDR policy.
DELETE /api-keys/{id}
Revoke immediately while retaining credential history.
Agents & SDKs

One contract for people and machines

This page is server-rendered with semantic headings, tables, code and stable fragment IDs, and the same reference is available as OpenAPI and Markdown.

The root llms.txt links these files so agents can reach the integration contract from the site index.