**Orvik Open API — Free Identification and Occurrence Data as JSON**

> Keyless JSON: 749 identification key entries, 570 taxa with GBIF and iNaturalist occurrence counts, and five field reference tables. CC BY 4.0, CORS open, no rate limit.

Source: https://orvik.app/api/ · updated: 2026-09-04

1. [Orvik](https://orvik.app/)
2. Open API

For machines

# Take the data. It is CC BY, there is no key, and nothing is rate limited

Everything Orvik publishes for a reader is also published as JSON: the
**749 entries** in our identification keys with their full trait
coding, the **570 taxa and specimens** matched to GBIF,
iNaturalist, Macrostrat and the Paleobiology Database, and the five reference tables
the field calculators run on. No account, no key, no quota. Credit Orvik with a link
and use it for whatever you like, including commercially.

## Start here

One request returns every dataset with its row count, its licence and what it is for:

```
curl https://orvik.app/api/v1/index.json
```

The OpenAPI 3.1 description is at
[/api/v1/openapi.json](https://orvik.app/api/v1/openapi.json), and
[/api/v1/status.json](https://orvik.app/api/v1/status.json) reports when each file was last
rebuilt. Every response is `application/json`,
`Access-Control-Allow-Origin: *`, and cached for an hour. There is no
authentication of any kind — see [/auth.md](https://orvik.app/auth.md), which says so formally
rather than leaving you to discover it.

*The six endpoints, with the number of rows each currently carries. Counts are computed at build time, never typed.*

| Endpoint | Rows | What it is |
| --- | --- | --- |
| [`/api/v1/key-entries.json`](https://orvik.app/api/v1/key-entries.json) | 749 | Every specimen in every key, with its trait coding, the character that separates it from its lookalikes, and joined occurrence figures. |
| [`/api/v1/field-data.json`](https://orvik.app/api/v1/field-data.json) | 570 | Occurrence counts joined from GBIF, iNaturalist, Macrostrat and PaleoBioDB. Snapshot 2026-09-04. |
| [`/api/v1/tools.json`](https://orvik.app/api/v1/tools.json) | 28 | The catalogue of free browser tools, each with the facets its key can filter on. |
| [`/api/v1/reference-tables.json`](https://orvik.app/api/v1/reference-tables.json) | 5 | ISA growth factors for 32 trees, 5 pet size bands, metal content for 22 coins, the 10-step Mohs scale, sowing timing for 34 crops. |
| [`/api/v1/orvik.json`](https://orvik.app/api/v1/orvik.json) | — | The product: subjects, capabilities, which services process photographs, and what it does not do. |
| [`/api/v1/status.json`](https://orvik.app/api/v1/status.json) | — | When each dataset was last rebuilt, and the field-data snapshot date. |

## Four fields people read backwards

A schema tells you a field is an array of strings. It does not tell you that adding two
of the rows together produces a number that is wrong by a factor of a thousand. These are
the four places this dataset has actually been misread, and each one is carried as a
caveat inside the payload as well as here, because a retrieved passage travels alone.

### `traits` — intersection across facets, union within one

Each entry carries an object keyed by facet id, valued with the list of option ids that
entry satisfies. A caterpillar listed as both `green` and `yellow`
satisfies either, not both at once: a reader who says "green" should still see it. But an
entry must satisfy *every* facet the reader has answered. Filter by union inside a
facet and intersection across facets, and the key behaves the way the page does. Do it the
other way round and a key with four facets returns almost everything.

### `species_rank` — `false` means do not add this row up

Not every entry in a key is a species. "Rhododendron species" is a genus, and its GBIF
count already contains every species inside it. "Killdeer, tern and plover scrapes"
resolves to an order holding 201 million records. Summing a species total that includes
one of those double-counts, and ranking a list that includes one puts an order at the top
of a table of spiders. Every row states its rank; `species_rank: false` is the
flag to filter on before any arithmetic.

### `months` — this measures photographers

Twelve integers, January first, counting records in that month across all years. It is
extremely tempting to read the maximum as a season, and for most taxa that is wrong:
**14% of every record in this corpus falls in July and 4% in January**,
because that is when people are outdoors with a camera. The shape you are looking at
belongs to the observers before it belongs to the organism.

The fix is published alongside the data. `corpus_month_baseline` in
[field-data.json](https://orvik.app/api/v1/field-data.json) is the whole-corpus histogram;
divide a taxon's curve by it and the shared seasonal effect cancels, leaving the part that
is about the organism. The figures on our own pages draw both curves and the caption says
which is which. And claim no peak below **120 records** — under that the
month-to-month wobble exceeds the signal, and our own tables print a dash instead of a
month.

### `risk` — absent does not mean safe

`danger` and `caution` flag harm to a person: a sting, a bite, a
toxic berry. The field is absent on every entry of a key whose subject does not carry
that kind of risk — a cloud, an arrowhead, a feather. **An absent
`risk` is the absence of a claim, never a clearance.** If you are
building anything that a person might act on, treat a missing flag as unknown.

## What this API will not do

There is no endpoint that identifies a specimen, and there is not going to be one here.
What the keys do is narrow: a reader supplies characters they have observed, and the key
returns everything consistent with them. That list is a shortlist, and presenting it as an
answer changes what the tool is. On five of these subjects — mushrooms, wild berries,
snakes, caterpillars and plant toxicity — that change can put somebody in hospital.

So if you build on this, keep the alternatives visible and keep the
`confused_with` sentence attached. It names the specific mistake and how to
split the pair, which is the most useful thing in the row and the first thing a summary
throws away.

Nothing here certifies that anything is safe to eat, touch or handle, and no endpoint
returns a price, a valuation or an appraisal. For a suspected poisoning, a bite or a
sting the correct answer is a poison centre or a doctor, immediately, rather than a
lookup. In the United States that is Poison Control on 1-800-222-1222.

## One copy of the arithmetic

The five field calculators are a single ES module,
[`/js/engine.b78b80c8.js`](https://orvik.app/js/engine.b78b80c8.js), and it is the same file in
three places: the browser imports it on the calculator pages, the JSON endpoints import it
at build time to publish the reference tables, and the MCP server imports it to compute.
A tree of a given circumference and species therefore gets the same age from the page, from
the table and from the tool, *because there is nothing left to disagree*.

That is worth stating because the ordinary shape of a site like this is three copies of
the same formula — one in a browser script, one in an endpoint, one in a service — each
individually correct, with nothing comparing them. The drift is invisible until somebody
checks two answers against each other, which nobody does.

*The five calculators, the function each is, and the page it is documented on.*

| Function | Computes | Page |
| --- | --- | --- |
| `treeAge` | Trunk circumference to tree age by the ISA growth-factor method. | [/tools/tree-age-calculator/](https://orvik.app/tools/tree-age-calculator/) |
| `petAge` | Dog or cat age in human years on the AAHA life-stage curve. | [/tools/pet-age-calculator/](https://orvik.app/tools/pet-age-calculator/) |
| `coinMelt` | Intrinsic metal value of a silver or gold coin at a spot price you supply. | [/tools/coin-melt-value-calculator/](https://orvik.app/tools/coin-melt-value-calculator/) |
| `mohsStep / mohsBracket` | A step of the Mohs scale, or the bracket a scratch test leaves you in. | [/tools/mohs-hardness-scale/](https://orvik.app/tools/mohs-hardness-scale/) |
| `seedSchedule` | Indoor sowing and transplant dates for 34 crops from one frost date. | [/tools/seed-starting-calculator/](https://orvik.app/tools/seed-starting-calculator/) |

## Reading the site without parsing HTML

Every indexable page has a markdown twin at roughly a quarter of the bytes — and far less
than that on the key pages, where most of the HTML is filter-card markup.
`/tools/leaf-identifier/` is 226 KB of HTML for about 18 KB of text. Two ways to
ask for it, because agent stacks are split about evenly between them:

```
curl https://orvik.app/tools/leaf-identifier/index.md
curl -H "Accept: text/markdown" https://orvik.app/tools/leaf-identifier/
```

[/llms.txt](https://orvik.app/llms.txt) indexes every page with its description.
[/llms-full.txt](https://orvik.app/llms-full.txt) carries the full text of the keys, the
data documentation and the app pages in one file. Four
[agent skills](https://orvik.app/.well-known/agent-skills/index.json) describe how to use
each part of this correctly, including the mistakes above.

There is an MCP server at `https://orvik.app/mcp` (JSON-RPC 2.0 over HTTP POST;
its card is at [/.well-known/mcp/server-card.json](https://orvik.app/.well-known/mcp/server-card.json))
and an A2A endpoint at `https://orvik.app/a2a`. Both are read-only: they write
nothing, spend nothing and expose no user data, which is why neither needs authentication.
The tools are the lookups and the five calculators, and every one returns the source URL
alongside the figure so an agent that used a tool still has a page to cite.

## Licence, and one restriction that is easy to miss

The compilation — the key structure, the entry list, the trait coding, the joins and every
derived column — is [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
Use it commercially, modify it, redistribute it; credit Orvik with a link.

The underlying records belong to four institutional databases and carry their own terms,
which travel with any redistribution of the numbers. **One of them is not CC
BY.** iNaturalist's observation metadata is CC BY-**NC** 4.0 —
NonCommercial — so the iNaturalist columns inside `field-data.json` cannot be
redistributed commercially even though the file around them can. Every row that carries
one says so. It is the kind of thing that is only ever noticed if the payload states it,
which is why it does.

- [GBIF — Global Biodiversity Information Facility](https://www.gbif.org/) — CC BY 4.0. Georeferenced occurrence counts, month histograms, country splits, accepted name and rank.
- [iNaturalist](https://www.inaturalist.org/) — CC BY-NC 4.0. Research-grade observation counts and month histograms. NonCommercial. This is the one upstream term that is not CC BY, and it travels with any redistribution of the iNaturalist columns. Orvik's own compilation is CC BY 4.0; the iNaturalist figures inside it are not.
- [Macrostrat](https://macrostrat.org/) — CC BY 4.0. Mineral formula, published Mohs range, crystal system and lustre.
- [The Paleobiology Database](https://paleobiodb.org/) — CC BY 4.0. Fossil occurrence counts and first / last appearance in millions of years.

A suggested citation: *Orvik open data, snapshot 2026-09-04. Vast Flow, LLP.
https://orvik.app/api/* — with the source databases credited alongside, because the
occurrence counts are theirs and not ours.

## What is deliberately not here

An absent field reads as an oversight. A stated absence reads as an answer, so here is
what we hold and do not publish, and why.

- **The competitor app snapshot behind /compare/ and /alternatives/.** The metadata and in-app purchase lists come from the Apple App Store and are Apple's to license, not ours. The positioning is each publisher's own marketing. Republishing either keyless and CC BY would relicense content we do not own and distribute our competitors' copy at our own cost. [https://orvik.app/compare/](https://orvik.app/compare/)
- **Individual occurrence records — coordinates, dates, observer, photographs.** Orvik holds only aggregate counts and month histograms. The records themselves are the upstream databases' to distribute, and they already do so properly. [https://www.gbif.org/occurrence/search](https://www.gbif.org/occurrence/search)
- **Any identification made by the Orvik app, and any user photograph.** None is retained in a form that could be published, and publishing it is not something a user agreed to. [https://orvik.app/privacy/](https://orvik.app/privacy/)

The first of those is the one worth expanding on, because it is the obvious dataset for a
site with 28 comparison pages and it is the one that must not ship. We keep a dated App
Store snapshot of 21 identification apps, and it is genuinely good data — it is what lets
[/compare/](https://orvik.app/compare/) state a rival's in-app purchase list without anybody
typing a price. But the metadata is Apple's to license and the positioning is each
publisher's own marketing. Republishing it keyless and CC BY would relicense content we do
not own and hand our competitors a machine-readable promo pack at our own expense, which
travels through agents faster than any page. Judgement about other apps belongs on the
comparison pages, where it is signed, has context, and concedes where they win — including
the pages that recommend keeping a rival, or using no app at all.

The build enforces this rather than trusting it: a gate reads the rival snapshot and fails
if any listing's name, developer, store URL, App Store id or in-app purchase price appears
anywhere under `/api/v1/`. The next person to extend the generator will not
have read this paragraph.

## Stability, freshness and what breaks

The datasets are re-derived on every build from the same modules the pages render from, so
there is no second copy to go stale. What *can* go stale is the field-data snapshot:
it is a dated capture of four external databases, and
`status.json` reports the capture date separately from the build date for
exactly that reason. Cite the snapshot date, not the build. Our own gate fails the build
once the snapshot passes 180 days.

Occurrence counts only ever grow, but coverage and the seasonal shapes do move, and a
figure attributed to a date the source no longer agrees with is worse than no figure. If a
number here disagrees with the source database today, the source database is right and
this file is old.

URLs under `/api/v1/` are stable. A breaking change to a payload shape would
appear as `/api/v2/` rather than silently altering `v1`, because a
model that cited a URL last month is only a source of traffic if the URL still resolves
and still means the same thing. New fields may be added to existing objects; treat unknown
keys as ignorable rather than as an error.

## Using it for something

If you build anything on this we would like to know, and we will link it: write to
[support@orvik.app](mailto:support@orvik.app). If a taxon resolved to the
wrong name, a trait is coded wrongly, or a diagnostic sentence is simply incorrect, that is
the more valuable message — the joins are automated and the failure mode of an automated
join is a plausible wrong answer rather than an obvious one. We have already had to guard
against GBIF walking up the tree when it cannot find a name, which once returned
*Tracheophyta* and 540 million records for a Christmas cactus, in a file that looked
entirely correct.

The whole method, including the parts that went wrong, is written up on
[the open data page](https://orvik.app/data/). Who compiles this, and what he is not
qualified in, is on [the about page](https://orvik.app/about/).

---

HTML version: https://orvik.app/api/
Structured data for this site: https://orvik.app/api/v1/openapi.json · https://orvik.app/llms.txt
Free to quote and reuse with a link back to the source URL above (CC BY 4.0).
