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.
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.
Search by number, by expression or by words. This is what the search bar uses.
| q | the term: a decimal, a fraction, a p-adic, a polynomial, an expression such as pi, or words |
|---|---|
| returns | matching numbers, and the tables and tags whose text matched |
Values held too imprecisely to identify anything, and values nobody has reviewed, are left out of search by number and marked on their table instead. A reader looking at a table can weigh an unconfirmed entry; somebody typing digits cannot, because a wrong fortieth digit looks exactly like a right one.
Look up a number whose type you already know, given its components, or several at once.
| kind | ZZ, QQ, RIF, RBF, CIF, Qp, polynomial |
|---|---|
| returns | the 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.
One table, whole.
| id | its T-number, e.g. T7 |
|---|---|
| url | or its slug |
| returns | the 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.
One tag, and what carries it.
| url | the tag, e.g. elliptic+curves |
|---|---|
| returns | its name, how many tables and numbers carry it, and which tables |
This page.
Add a table. The body is the whole document, as YAML or JSON.
| X-Produced-By | what made this, recorded on the revision. Defaults to api |
|---|---|
| X-Edit-Message | what changed, for the history |
| returns | 201 with the allocated tid and the first revision |
| 400 | no title, no entries, or a document that is not a table |
| 413 | over 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.
Replace a table's document. PUT does the same.
| X-Base-Revision | the 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-By | as above |
| 409 | a stale edit whose changes overlap, or a change to the table's parameters |
| 413 | over 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.
The entries alone, leaving the prose untouched. This is what a generator wants.
| X-Entries-Mode | replace (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-Id | names one run. Submissions carrying the same run grow a single revision instead of adding one each |
| X-Produced-By | as above |
| returns | the revision, and for an upsert how many entries were added and updated |
| 429 | another 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.
Claim a table for the length of a run, refresh the claim, or drop it.
| X-Run-Id | the run the claim belongs to, so a resumed run keeps it |
|---|---|
| X-Lease-Note | what is being done, shown to whoever is turned away |
| returns | whether it is held, and when it expires |
| 409 | somebody 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.
Attach a file — usually the code that produced the numbers. The body is the file.
| X-Run-Id | carrying the run of the entries puts the file on the same revision as them |
|---|---|
| 400 | a name with a directory in it, a hidden name, or no body |
| 413 | over 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.
$ 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.