API reference
Base URL: https://uselayer.sh. All responses are JSON.
Authentication
Every endpoint except /v0/health needs a key from your dashboard:
authorization: Bearer lyr_your_key
x-api-key: lyr_your_key also works. Each key allows 60 requests per minute.
GET /v0/matches
Every live match, soonest event first. Use it to discover what Layer can answer. For example, this weekend's NFL matches: ?category=sports&q=nfl&from=2026-10-03&to=2026-10-05. A filter it can't read, such as from=Oct 5, returns 400 bad_request.
| Query | Default | Meaning |
|---|---|---|
limit |
50 |
1–200 |
offset |
0 |
Skip this many matches first, to page past limit. |
category |
all | Only matches in that category, e.g. sports. A category with no live matches yet returns count: 0. |
from |
— | Only events on or after this day (YYYY-MM-DD, the match's event_date). |
to |
— | Only events on or before this day. |
q |
— | Words that must all appear, ignoring case, somewhere in either venue's event title, question or outcome, or in Kalshi's series ticker or Polymarket's event slug (so league names such as nfl work). E.g. chiefs or fed december. Up to 200 characters. |
venue |
polymarket |
Which Polymarket the matches are with: polymarket (polymarket.com) or polymarket_us (Polymarket US, polymarket.us). |
Response
| Field | Meaning |
|---|---|
count |
Number of matches returned. |
matches[].kalshi |
The Kalshi market (see Market object). |
matches[].polymarket |
The Polymarket market. With venue=polymarket_us this field is matches[].polymarket_us instead. |
matches[].event_date |
The day the event happens (YYYY-MM-DD). When Kalshi only gives the date it settles by, this is Polymarket's earlier date for the event. |
matches[].category |
Layer's category for the event. |
matches[].confidence |
0–1. 1 when a person approved the match. |
matches[].basis |
identical or equivalent_with_caveats. |
matches[].caveats |
Which rules differ (see Overview). |
matches[].tier |
human_verified or auto_verified. |
attribution |
Where the data comes from (see Using Layer data). |
checked_at |
When this answer was produced. |
GET /v0/match
The twin of one market on the other venue.
| Query | Required | Meaning |
|---|---|---|
venue |
yes | kalshi, polymarket or polymarket_us — the venue of the id you pass. |
market_id |
yes | Kalshi: the market ticker. Polymarket: the conditionId, market slug, or numeric id. Polymarket US: the market slug; for a market with two named sides, <slug>:long or <slug>:short. |
with |
no | For a Kalshi market: which Polymarket to find its twin on, polymarket (the default) or polymarket_us. |
Match found
| Field | Meaning |
|---|---|
source_market |
The market you asked about. |
matched_market |
Its twin on the other venue. |
side |
Always same: YES on one pays like YES on the other. |
tier, confidence, basis, caveats |
As in /v0/matches. |
match_reason |
Who approved it and which outcomes were paired. |
attribution |
Where the data comes from (see Using Layer data). |
checked_at |
When this answer was produced. |
No match — matched_market is null and reason says why. source_market is still the market you asked about, or null when Layer doesn't know the id or can't tell which side you mean (specify_outcome).
reason |
Meaning |
|---|---|
not_indexed |
Layer doesn't know this id, or the market has closed. |
no_candidate |
Layer found nothing on the other venue that could be the same bet. |
pending_review |
A possible match exists but hasn't been approved yet. |
rejected |
A possible match was checked and is not the same bet. |
outcome_unmapped |
The events match, but this particular outcome has no twin. |
rules_changed_pending_review |
A venue edited its rules since approval; re-checking. |
specify_outcome |
This Polymarket market has two named sides; pass <conditionId>:0 or <conditionId>:1 (Polymarket US: <slug>:long or <slug>:short). |
Market object
| Field | Meaning |
|---|---|
venue |
kalshi, polymarket or polymarket_us. |
market_id |
The id to use with the venue's own API. |
group_id |
The venue's event id (Kalshi event ticker, Polymarket event slug). |
event |
The event's full title. |
question |
The market's own question. |
outcome |
The outcome this contract pays on. |
url |
The market's page on the venue, where it's traded. Link to it when you show the market. |
close_time |
When the venue stops trading this market at the latest (ISO 8601, UTC), or null if the venue gives none. It can close earlier once the result is known, so it's often later than event_date. |
resolution_sources |
Where the venue says it will look to settle the market: a list of { "name", "url" }, either of which can be null. Kalshi lists them per event; Polymarket gives a link and names it in its rules. Empty when the venue names none. When a match has the source_differs caveat, this is where the two sides differ. |
slug, yes_token_id |
Polymarket only: the market slug and the CLOB token id for YES — what you trade with. Polymarket US markets have slug only (without the :long / :short side). |
POST /v0/match
Look up many markets in one call: up to 50, from either venue, mixed freely. The whole call counts as one request against your key's 60 per minute.
curl -X POST -H "authorization: Bearer $LAYER_KEY" \
-H "content-type: application/json" \
-d '{"markets":[{"venue":"kalshi","market_id":"KXFEDDECISION-26OCT-C25"},{"venue":"polymarket","market_id":"afcq-ben-mau-2026-09-29-ben"}]}' \
"https://uselayer.sh/v0/match"
Request body
| Field | Meaning |
|---|---|
markets |
1–50 lookups. |
markets[].venue |
kalshi, polymarket or polymarket_us, as in GET /v0/match. |
markets[].with |
Optional, as in GET /v0/match. |
markets[].market_id |
Any id GET /v0/match accepts. |
Response
| Field | Meaning |
|---|---|
count |
Number of results, the same as the number of markets you sent. |
results |
One per market, in the order you sent them. Each has the venue and market_id you sent, plus exactly the fields GET /v0/match returns: a match, or matched_market: null with a reason. |
attribution, checked_at |
As in GET /v0/match, once for the whole answer. |
A market Layer doesn't know comes back as not_indexed in its place in results; it doesn't fail the call. The call fails with 400 bad_request only if the body is malformed, and detail names the entry (for example markets.3.venue).
POST /v0/profit
Is buying one side on each venue profitable once fees are paid? You send the prices; Layer applies each venue's official fee schedule. Buying YES on one venue and NO on the other, for the same matched outcome, pays exactly $1 per contract however it settles. So the trade is profitable when the two prices plus both fees come to less than $1 per contract.
The trade is Kalshi plus one other venue: send kalshi and exactly one of polymarket or polymarket_us.
curl -X POST -H "authorization: Bearer $LAYER_KEY" \
-H "content-type: application/json" \
-d '{"contracts":100,"kalshi":{"price":0.42},"polymarket":{"price":0.55,"fee_rate":0.05}}' \
"https://uselayer.sh/v0/profit"
Request body
| Field | Default | Meaning |
|---|---|---|
contracts |
required | Contracts bought on each venue, a whole number up to 1,000,000. |
kalshi.price |
required | What you pay per contract on Kalshi, in dollars (0.42 is 42¢). Up to 6 decimal places. |
kalshi.role |
taker |
taker if your order fills against the book, maker if it rests first. |
kalshi.fee_type |
quadratic |
The series' fee_type from Kalshi's API: quadratic, quadratic_with_maker_fees or quadratic_with_combo_maker_fees. Only the last two charge makers. |
kalshi.fee_multiplier |
1 |
The series' fee_multiplier from Kalshi's API. Some series use 0.5, and fee-free ones use 0. |
polymarket.price |
required | What you pay per share on Polymarket, in dollars. |
polymarket.role |
taker |
Polymarket charges takers only, so maker pays no fee. |
polymarket.fee_rate |
— | The market's feeSchedule.rate from Polymarket's API. Send this or category. |
polymarket.category |
— | Uses Polymarket's default rate for the category: crypto 0.07; sports, economics, culture, weather, other 0.05; finance, politics, mentions, tech 0.04; geopolitics (or world) 0. |
polymarket.exponent |
1 |
The market's feeSchedule.exponent. |
polymarket_us.price |
required | What you pay per contract on Polymarket US, in dollars. Buying NO at X is a YES order at 1 − X there; send X. |
polymarket_us.role |
taker |
taker pays the fee; maker gets Polymarket US's rebate. |
polymarket_us.fee_coefficient |
0.0695 |
The market's feeCoefficient from Polymarket US's API: its taker rate. |
price is the price of the side you buy on that venue. Pass the YES price on one venue and the NO price on the other.
Response
{
"contracts": 100,
"kalshi": { "price": 0.42, "role": "taker", "cost": 42, "fee": 1.71, "fee_rate": 0.07, "fee_multiplier": 1, "fee_type": "quadratic" },
"polymarket": { "price": 0.55, "role": "taker", "cost": 55, "fee": 1.2375, "fee_rate": 0.05, "exponent": 1 },
"payout": 100,
"spread": 0.03,
"gross_profit": 3,
"fees": 2.9475,
"net_profit": 0.0525,
"return_pct": 0.05,
"profitable": true
}
| Field | Meaning |
|---|---|
kalshi.cost, polymarket.cost |
Price × contracts on each venue. The second leg is under the venue you sent: polymarket or polymarket_us, with its fee_coefficient in place of fee_rate and exponent. |
kalshi.fee, polymarket.fee |
Each venue's fee for the order, in dollars. |
payout |
$1 × contracts: what the pair pays whichever way it settles. |
spread |
Per contract, before fees: $1 − Kalshi price − Polymarket price. |
gross_profit |
Payout − both costs, before fees. |
fees |
Both fees together. |
net_profit |
Payout − both costs − both fees. Negative means you'd lose money. |
return_pct |
Net profit as a percentage of everything you pay (costs plus fees). |
profitable |
true when net_profit is above zero. |
How fees are worked out
- Kalshi:
multiplier × rate × contracts × price × (1 − price), rounded up to the next cent. The rate is 0.07 for takers. Makers pay 0.0175, or 0.035 on combo series, but only on series whosefee_typeincludes maker fees. This follows Kalshi's fee schedule and matches its published fee table. Members whose balance is kept to $0.0001 may pay up to 1¢ less per order. - Polymarket:
contracts × fee_rate × (price × (1 − price))^exponent, rounded to 5 decimal places, takers only (Polymarket's fees). - Polymarket US:
fee_coefficient × contracts × price × (1 − price), rounded to the nearest cent with ties to even. Makers get a rebate of0.0125 × contracts × price × (1 − price), shown as a negative fee. This follows Polymarket US's fee schedule and matches its published 100-lot table. Its volume rebates, paid weekly, aren't included.
You send the prices, so no venue data is served: /v0/profit keeps working when a venue's data is switched off (see Errors).
The answer assumes each order fills in full at the price you send, as a single order. It leaves out deposit and withdrawal costs, rebates, and any price movement while you trade. Fees change: if a venue's schedule differs from this, the venue's schedule wins.
GET /v0/health
No key needed. Is the API up and can it reach its database?
{
"ok": true,
"database": { "reachable": true, "ms": 62 },
"venues": {
"kalshi": { "open_events": 12587, "open_markets": 125458, "last_pull": "…" },
"polymarket": { "open_events": 42935, "open_markets": 364647, "last_pull": "…" },
"polymarket_us": { "open_events": 4032, "open_markets": 65561, "last_pull": "…" }
},
"live_market_pairs": 14,
"live_market_pairs_by_venue": { "polymarket": 14, "polymarket_us": 9 },
"checked_at": "…"
}
live_market_pairs counts live matches with polymarket.com; live_market_pairs_by_venue counts them for each Polymarket.
Errors
| Status | error |
When |
|---|---|---|
| 400 | bad_request |
Missing or invalid query parameters or body; detail says which field. |
| 401 | unauthorized |
No key, or the key is unknown or revoked. |
| 403 | venue_not_served |
Matches with that venue (venue) aren't available right now. |
| 429 | rate_limited |
Over 60 requests/minute. Wait retry-after seconds. |
| 503 | venue_unavailable |
Data from a venue is temporarily switched off (venues lists which), so matches are paused. |
| 503 | database_not_configured / database_unreachable |
Layer can't reach its database. |
Using Layer data
Every /v0/matches and /v0/match answer includes an attribution line. The data comes from Kalshi and Polymarket, and trading happens on each venue. When you show a market to your users, credit the venue and link to its url. Use Layer data inside your own product; don't republish it as a standalone feed or aggregator. The full terms are at /terms.