Novig API (2026): Access, Keys and Documentation

The Short Answer
The Novig API is a signed REST and WebSocket API for trading sports contracts on Novig, documented at docs.novig.com. After an identity check, you create a management key in the Novig app under Profile, Settings, Novig API. That key opens subaccounts, and each subaccount trades with its own key. Signed requests carry Novig-Key-Id, Novig-Timestamp and Novig-Signature headers, signed with Ed25519 or P-256 under a scheme Novig calls NOVIG-V3. Market data under /v3/public needs no key. Production runs at https://api.novig.com, and Paper, Novig's play-money exchange, at https://api.paper.novig.com.
Key Takeaways
- A management key moves money but never trades. A trading key trades one subaccount but never moves money.
- Keys are keypairs, and Novig stores only the public half.
- Public routes for events, markets, order books and trades need no key and are throttled per IP address.
- Signed routes run two location checks and answer
451when one fails. Cancels skip both. - Rate limits are token buckets per key, such as a burst of 256 order placements that refills at 8 a second.
What is the Novig API?
Novig is a sports exchange, the trading name of Ludlow Exchange, LLC, which Novig's documents describe as a CFTC-designated contract market. Its current API is version 3, where you "place orders with signed REST requests" and "watch the order book and your fills on one websocket."
Everything tradable nests: a sport and a league classify an event, an event holds markets, and a market holds outcomes. An order always names an outcome. Prices are probabilities sent as strings, such as "0.665", and one contract pays 1 cent if its outcome wins.
Novig's docs still carry the previous NBX API, which used OAuth 2.0 client credentials and 30-minute access tokens, but file it under a deprecated section.
How do you get a Novig API key?
You create keys yourself. As of October 2026, the steps are:
- Pass the identity check. Production runs a real one. Paper, at
paper.novig.com, skips it and starts every account with $10,000 in play money. - Create the management key. Open Profile, then Settings, then Novig API, press Create Key and name it. The browser generates an Ed25519 keypair, or P-256 if it can't, and downloads
novig-api-key-<nickname>.pem. - Save the private key. Novig shows it once and never holds it, so store it in a secret manager first.
- Open a subaccount. Send the public half of a second keypair to
POST /v3/account/subaccounts, signed by the management key. ThekeyIdthat comes back names both the trading key and the subaccount.
| Scope | Reaches | What it can do |
|---|---|---|
management | The whole account | Open, fund and label subaccounts, and create and revoke keys. Never places orders |
management::read | The whole account | Read keys, subaccounts, balances and ledgers |
trading | One subaccount | Place and cancel orders, read the catalog and stream. Never moves money |
trading::read | One subaccount | Read its orders, fills, positions, balance and the catalog |
You can hold one live management key and up to five live subaccounts, each with one live trading key. Liquidity providers can also onboard directly: Novig's LP page says to email developers@novig.com with a W-9, promises test-environment access within 2 business days, and generally requires a $30,000 minimum deposit before production.
How does Novig API authentication work?
Every signed request, GET included, carries the three headers. The signature covers six lines joined by line feeds, with no trailing newline:
NOVIG-V3
{unix_millis}
{METHOD}
{path}
{canonical_query}
{lowercase_hex(sha256(raw_body))}
The timestamp must sit within 30 seconds of Novig's clock, and the path is signed exactly as sent, so /v3/keys and /v3/keys/ differ. The query is decoded, re-encoded with uppercase hex and sorted by name, then value. This signer does it all with Node 18 or newer and no packages, saved as an .mjs file next to your key:
import { createHash, createPrivateKey, sign } from 'node:crypto';
import { readFileSync } from 'node:fs';
const HOST = 'https://api.paper.novig.com';
const KEY_ID = 'YOUR_KEY_ID';
const privateKey = createPrivateKey(readFileSync('novig-api-key.pem'));
const cmp = (x, y) => (x < y ? -1 : x > y ? 1 : 0);
const encode = (part) =>
[...Buffer.from(decodeURIComponent(part))]
.map((b) => (/[A-Za-z0-9._~-]/.test(String.fromCharCode(b)) ? String.fromCharCode(b) : '%' + b.toString(16).toUpperCase().padStart(2, '0')))
.join('');
function canonicalQuery(raw) {
const pairs = raw.split('&').filter(Boolean).map((pair) => {
const i = pair.indexOf('=');
return i < 0 ? [encode(pair), ''] : [encode(pair.slice(0, i)), encode(pair.slice(i + 1))];
});
pairs.sort((a, b) => cmp(a[0], b[0]) || cmp(a[1], b[1]));
return pairs.map(([name, value]) => `${name}=${value}`).join('&');
}
function stringToSign(ts, method, path, query, body) {
const hash = createHash('sha256').update(body).digest('hex');
return ['NOVIG-V3', ts, method, path, canonicalQuery(query), hash].join('\n');
}
async function novig(method, path, query = '', payload) {
const body = payload === undefined ? '' : JSON.stringify(payload);
const ts = Date.now().toString();
const signature = sign(null, Buffer.from(stringToSign(ts, method, path, query, body)), privateKey).toString('base64');
const q = canonicalQuery(query);
const res = await fetch(HOST + path + (q ? `?${q}` : ''), {
method,
body: body || undefined,
headers: {
'Novig-Key-Id': KEY_ID,
'Novig-Timestamp': ts,
'Novig-Signature': signature,
'Content-Type': 'application/json',
},
});
return { status: res.status, body: await res.text() };
}
console.log(await novig('POST', '/v3/echo', '', { hello: 'world' }));
We ran this exact code against all 30 test vectors Novig publishes: every canonical string matched, every Ed25519 signature matched byte for byte, and every P-256 signature verified. POST /v3/echo returns your body with a 200 once host, key, clock and signature are right. In our checks on 8 October 2026, a correctly signed echo from an unregistered key got 401 with "api key not found" on both hosts. Keep the Content-Type header, because without it Novig hashes zero bytes instead of your body.
On production, every signed route also runs two location checks, and Paper runs the same API without them. The IP check refuses restricted states and VPN, proxy or Tor traffic, though a data-center address is fine. The companion check reads the key holder's last device geolocation from the Novig app, and placing an order needs one from the last 3 days. A failure returns 451 with a code such as GEOLOCATION_EXPIRED.
What endpoints does the Novig API have?
| Route | What it does | Key |
|---|---|---|
GET /v3/public/catalog/events, /markets | Open events and markets, filtered by league, market type or start time | None |
GET /v3/public/catalog/markets/{id}/book | Resting orders for each outcome, best price first | None |
GET /v3/public/catalog/markets/{id}/trades | The public tape, newest first | None |
GET /v3/public/types/sports, /leagues, /markets | The fixed lists of sports, leagues and market types | None |
POST /v3/echo | Tests your signature | Any |
POST /v3/account/subaccounts, /{keyId}/transfer | Open and fund a subaccount | Management |
POST /v3/orders, /v3/orders/batch | Place one order, or up to 256 accepted all or nothing | Trading |
DELETE /v3/orders/{id}, /v3/orders | Cancel one order, or all of them, optionally by event, market or outcome | Trading |
GET /v3/portfolio/fills, /positions | Your fills, with fees, and your positions | Trading or trading::read |
GET /v3/limits | Your throttle schedule, free to call | Any |
GET /v3/ws | The WebSocket | Trading or trading::read |
Listings return up to 5,000 rows a page, 500 by default, with a next cursor you pass back as after. A 201 on an order means queued, not resting: it rests when its open event arrives on the private stream.
Every resting order on Novig is a bid to buy an outcome. A bid on one outcome is liquidity for the other at 1 minus its price, so the cheapest way to buy an outcome now is 1 minus the best bid on the other side. This prints both for five NFL moneylines, with no key:
const HOST = 'https://api.novig.com';
const get = async (path) => (await fetch(HOST + path)).json();
const milli = (price) => (price === undefined ? null : Math.round(Number(price) * 1000));
const fmt = (m) => (m === null ? '-' : (m / 1000).toFixed(3));
const { items: markets } = await get('/v3/public/catalog/markets?league=NFL&marketType=MONEY&limit=5');
for (const market of markets) {
const { orders } = await get(`/v3/public/catalog/markets/${market.marketId}/book`);
const best = market.outcomes.map((o) => milli(orders[o.outcomeId]?.[0]?.price));
market.outcomes.forEach((o, i) => {
const other = best[1 - i];
console.log(o.name.padEnd(4), 'bid', fmt(best[i]), 'offer', fmt(other === null ? null : 1000 - other));
});
}
Thousandths keep the subtraction exact. Each outcome's array lists the best price first, then the earlier order within a price, so the array is the queue. In our run on 3 October 2026, the first game, Buffalo at the Los Angeles Rams, printed a Rams bid of 0.520 and an offer of 0.560. The book also returns an ETag, and a matching If-None-Match answers 304. In our checks on 8 October 2026, the servers behind the API each issued their own ETag for the same unchanged book, so one saved value matched on 3 of 24 repeat requests. Treat a 304 as a bonus, not a way to budget reads.
What are the Novig API rate limits?
Novig throttles each key with token buckets, and an edge filter limits each IP address and caps body size. Each bucket's capacity is your burst:
| Throttle | Burst | Refill | Spent by |
|---|---|---|---|
place | 256 | 8 a second | Order placement, one token per order in a batch |
cancel | 256 | 16 a second | Cancels |
read | 64 | 16 a second | Catalog, order and position reads |
account | 64 | 8 a second | Key, subaccount and transfer routes |
stream | 512 | 4 a second | WebSocket subscriptions, by weight |
history | 512 | 4 a second | Fills, transactions and settled orders, priced per page |
Going over returns 429 with a Retry-After header. The edge refuses with a 403 and an HTML body instead. Throttles count per key, so splitting work across keys raises your ceiling.
How does the Novig WebSocket work?
One signed connection at GET /v3/ws, on the same host as REST, carries every channel. Subscriptions spend the stream bucket by weight, per market or event: 1 for lifecycle, 4 for trades, 8 for bbo and 16 for book, plus 1 each for your private orders and positions. A connection can watch up to 2,048 markets. Each subscription starts with a snapshot carrying a seq, deltas follow with no gap, and a skipped seq means resync with a snapshot request. A trading key can also place and cancel orders on the same connection with the place, cancel and cancel_all verbs, which spend the place and cancel buckets rather than the stream one. Watch lifecycle closely: GOLIVE voids every resting order on a market, and it can repeat after a delay or a review. How streaming works across venues is covered in how to stream live prediction market prices.
How do Novig prices and fees work?
At 1 cent a contract, 110 contracts at 0.665 cost $0.7315 and pay $1.10 if they win. Prices sit on a grid of 279 values: steps of 0.001 from 0.001 to 0.050 and from 0.950 to 0.999, and steps of 0.005 in between. Every order buys an outcome, so snap model prices down onto the grid, because anything off it is refused with INVALID_PRICE.
Takers pay c × P × (1 - P) cents per contract at fill price P, and makers never pay. Game markets use a coefficient of 0.03 and charge only while the event is live, and NFL, MLB and NCAAF futures use 0.06 at all times. In our checks on 3 October 2026, every NFL moneyline we pulled carried a fee object with a coefficient of 0.03 and WHEN_LIVE charging. Fills can still go against you, so test on Paper's play money before you size up.
Where can you get Novig historical data?
Novig publishes two anonymized CSV files per trading day at data.novig.com, with no key: trades.csv, one row per side of every trade, and markets.csv, with each listed market's open interest, volume and OHLC. A manifest at /reporting/trade-data/index.json lists the dates, and each day lands around 5 a.m. Eastern the next morning.
In our checks on 3 October 2026, the 1 October file held 191,489 trades and about $60.5 million in notional volume, counted Novig's way from the taker rows, and parlays made up about half of it. The CSVs count a contract as paying $1, while the API counts 1 cent contracts.
For one market or event after it closes, the API also has signed history routes, GET /v3/history/markets and GET /v3/history/events, which is where Novig says to read a closed market's grade. They spend the history bucket.
How do you compare Novig prices with other venues?
The same game often trades on other sports exchanges and prediction markets at a different price. The Predictefy API serves Novig through the same normalized schema as the rest of its 15+ venues, and its Novig books already show each outcome's offer as the complement of the other side's bids, the math the example above does by hand. For trading, Predictefy builds the Novig order server side, and the Novig credential you supply at submit is used for that one request and never stored. Which venues suit which sports is covered in the best prediction market for sports betting.
Frequently Asked Questions
Does Novig have an API?
Yes. The Novig API, version 3, places orders with signed REST requests and streams the order book and your fills over one WebSocket. Its documentation is at docs.novig.com.
How do I get a Novig API key?
Pass the identity check, then open Profile, Settings, Novig API in the Novig app and press Create Key. The browser generates the keypair and shows the private key once. That management key opens subaccounts, and each subaccount gets its own trading key.
Do I need an API key for Novig market data?
No. The routes under /v3/public, for events, markets, order books, trades and the type lists, are unsigned and throttled per IP. Novig's daily CSV files need no key either.
What are the Novig API rate limits?
Each key has token buckets: place holds 256 and refills at 8 a second, cancel 256 at 16, read 64 at 16, account 64 at 8, and stream and history 512 at 4. Going over returns 429 with a Retry-After header.
Does Novig have a test environment?
Yes. Paper is Novig's play-money exchange at https://api.paper.novig.com, with its own web app at paper.novig.com. It skips the identity check and the location checks, and every account starts with $10,000 in play money. Keys work only where you create them, so production rejects a Paper key with "api key not found".
Why does the Novig API return 451?
A location check failed. Signed routes refuse restricted states and VPN, proxy or Tor traffic, and they check the key holder's last device geolocation from the Novig app. Placing an order needs a geolocation from the last 3 days, while a cancel skips both checks.