API reference

A small JSON API. Reading needs nothing; writing needs a key, and an account whose edits have been reviewed. The numberdb package is the supported way to use all of this — it handles the response format, the retries and the crash-safety — and this page is what it is built on.

Keys, limits and permission Reading /api/search /api/lookup /api/table /api/tag /api/docs Writing /api/tables /api/table/<tid> /api/table/<tid>/entries /api/table/<tid>/lease /api/table/<tid>/file/<name>

Keys, limits and permission

Anonymous use is limited to 60 requests per hour; a key raises it to 1000. Send it as a bearer token, and keep it out of a worksheet you might share:

export NUMBERDB_API_KEY=...
curl -H "Authorization: Bearer $NUMBERDB_API_KEY" https://numberdb.org/api/table?id=T7

Exceeding the limit returns 429 with a Retry-After header. Keys are not self-service yet: write to info@numberdb.org.

Writing needs more than a key. It opens once 5 of the account's edits have been reviewed and accepted — edits made on the site count. A person editing one table exercises judgement about it; a program exercises none and writes faster than anyone can read, so the gate is a track record that a script cannot manufacture for itself.

Writing also enforces the size limits rather than warning about them, and refuses a document whose type or parameters do not make sense. A person over a soft limit is told and their edit is saved; a program is refused until the reason is in the document, because a warning shown to nobody is not a limit.

Reading

GET/api/lookup

Look up a number whose type you already know, given its components, or several at once.

kindZZ, QQ, RIF, RBF, CIF, Qp, polynomial
returnsthe matching entries, keyed by position when several were asked about

At most 100 numbers per call. Every position asked about is present in the answer, so a number that matched nothing maps to an empty list rather than being absent.

GET/api/table

One table, whole.

idits T-number, e.g. T7
urlor its slug
returnsthe document: its prose, its parameters and its entries

A draft answers as though it did not exist. Saying “you may not see this” would confirm that a table of that name is there, which is the one thing a private draft should not tell a stranger.

GET/api/tag

One tag, and what carries it.

urlthe tag, e.g. elliptic+curves
returnsits name, how many tables and numbers carry it, and which tables

GET/api/docs

This page.

Writing

POST/api/tables

Add a table. The body is the whole document, as YAML or JSON.

X-Produced-Bywhat made this, recorded on the revision. Defaults to api
X-Edit-Messagewhat changed, for the history
returns201 with the allocated tid and the first revision
400no title, no entries, or a document that is not a table
413over a size limit

A new table needs a title and at least one entry. One with none is a draft, and a published draft holds a permanent T-number, appears in the listings and answers nothing. The identifier is allocated here and ignored if you send one.

POST/api/table/<tid>

Replace a table's document. PUT does the same.

X-Base-Revisionthe revision you edited from. A concurrent change is then merged where it can be and refused where it cannot, instead of silently overwritten
X-Produced-Byas above
409a stale edit whose changes overlap, or a change to the table's parameters
413over a size limit

The whole document, so anything the body omits is removed. A program that computes values wants /entries instead: assembling a document from a title, the parameters and the numbers would delete the definition, the comments and the references, and the result would look perfectly ordinary.

A table's parameters cannot be changed once it is published. Every entry is identified by its parameter values, so changing them reassigns every identity at once and existing citations resolve to different numbers.

POST/api/table/<tid>/entries

The entries alone, leaving the prose untouched. This is what a generator wants.

X-Entries-Modereplace (default) or upsert. Upsert merges what you send into what is stored, by identity, so sending one entry leaves the other thousand alone
X-Run-Idnames one run. Submissions carrying the same run grow a single revision instead of adding one each
X-Produced-Byas above
returnsthe revision, and for an upsert how many entries were added and updated
429another write to this table held the lock for longer than 15s. Nothing was written; send it again

Every revision holds the complete document, so a revision per entry would be a thousand copies of the whole table. A run amends its own revision instead — which works only while that revision is still the latest, so a run that will take a while should hold a lease.

POSTDELETE/api/table/<tid>/lease

Claim a table for the length of a run, refresh the claim, or drop it.

X-Run-Idthe run the claim belongs to, so a resumed run keeps it
X-Lease-Notewhat is being done, shown to whoever is turned away
returnswhether it is held, and when it expires
409somebody else holds it, with their name and its expiry

It lasts 20 minutes and is refreshed by each submission, so a run whose entries are quicker than that needs no heartbeat. It expires because a generator that dies must not lock a table for good, and it never refuses a person editing on the site: a generator's claim is against other generators.

POST/api/table/<tid>/file/<name>

Attach a file — usually the code that produced the numbers. The body is the file.

X-Run-Idcarrying the run of the entries puts the file on the same revision as them
400a name with a directory in it, a hidden name, or no body
413over 2048 KB, or over 8192 KB for the table's files together

A table's files are flat: no directories, so there is one place to look and no question about where a path leads. They are recorded, never executed.

What the answers look like

$ curl -X POST https://numberdb.org/api/table/42/entries \
    -H "Authorization: Bearer $NUMBERDB_API_KEY" \
    -H "X-Entries-Mode: upsert" -H "X-Run-Id: zeta-2026-08" \
    --data-binary @entries.yaml

{"tid": "T42", "url": "Zeta_at_integers",
 "revision": "9f2c...", "unchanged": false, "amended": true,
 "entries": 1000, "added": 1, "updated": 0, "reviewed": true}

Every refusal carries an error saying what is wrong and, where there is more to say, a detail saying what to do about it.