The salary API that ships its own caveats
Key measurements
| Measure | Value | Source and date |
|---|---|---|
| Role records in the published salary dataset | 3,445 | September 7, 2026 · Orbyt Labs public salary roles dataset |
| Distinct base bands across those records | 513 | September 7, 2026 · Orbyt Labs public salary roles dataset |
| Sample size in the public demo compensation response | 0 | September 7, 2026 · Orbyt Intelligence public demo response |
You have a number on a screen. A base salary, a role, a city, and no idea whether it is good.
Ask Orbyt Intelligence and one sentence comes back. Not a table. One sentence, written to be lifted whole: an offer of that amount is above, at, or below market for a mid level AI engineer in San Francisco, with the market median beside it and the gap named. The playground runs against a demo endpoint that needs no key, so you can read the exact shape of that response before you own one.
Then the same response says, in its own sentence, that the number is a computed estimate and must not be cited as a wage.
Both sentences are the product. The one you want to quote, and the one you would rather skip.
Why does one dataset ship in two shapes?
REST is for code you write. Twenty endpoints, Bearer auth, cursor pagination, filtering as a comma list, field shaping, expansion, and a version header carrying a date. Every response arrives in a locked envelope defined by an internal RFC.
Locked means locked.
Once a field ships in v1 it never disappears. Field names never change. Types never change. Error codes can be added, never removed. The whole surface is written out in the API docs.
That commitment costs more than it looks like it costs. It means a badly named field is permanent. I would rather carry a mediocre name forever than break somebody's integration on a Tuesday.
MCP is for agents. It is a real server, documented on the MCP page. JSON-RPC over POST, with Bearer auth. It does not return the REST envelope. It returns a decision ready object.
The bet behind that shape is narrow and I made it deliberately. A model quotes the string that looks most like an answer, and it can quote that string without the object around it. So the qualification has to live inside the sentence, not in a sibling field.
What is actually inside the answer?
Six fields. The first one is the whole argument.
answer is one sentence. On a US compensation call the citation string beside it reads that Orbyt Intelligence models this role in this city at this figure, and then, in a second sentence, that this is a computed estimate rather than an observed wage and must not be cited as one. An adjective would not survive a summary. A sentence does. Dropping the caveat now means deleting a clause on purpose.
data splits into primary and supporting. The headline figure and the context it came from stay structurally apart, instead of sitting as two keys of equal weight.
context is the field that embarrasses us usefully. It carries the methodology version, a sample size, confidence bounds, a source breakdown that sums to one, and a disagreement flag. On a served US estimate the sample size is zero. The confidence bounds are the same low and high already in the response, not a sampling interval. The breakdown is one entry, computed_from_role_baseline at 1.0.
Those are the honest values. The machinery is there for the day there is a sample. Today it says there is none.
The disagreement flag is arithmetic, not a mood. When several sources are reconciled into one figure, it goes true if any contributing source sits more than a quarter away from the consensus value. With a single contributor it is false, because nobody is there to disagree.
citation holds a permanent link to the methodology page, a stable id you can resolve to a provenance trail, a corrections feed, and the request id of the upstream call.
follow_up_suggestions names the next tool, the arguments to call it with, and the reason. An agent that just got a compensation number usually wants the adjacent roles.
limitations is plain strings. Cost of living is adjusted by regional price parity and local tax is not factored in. Equity and bonus are modelled by role archetype. Ask for a country not served yet and the fallback is stated in the answer sentence itself, not only down here.
All of that rides along even when what you wanted was one number. That is a real cost, paid on purpose. The overhead is the provenance.
Why only six tools?
Six is a ceiling. Analyze compensation. Analyze market. Analyze skills. Discover roles and cities. Find adjacent opportunities. List capabilities.
Every tool you add is a tool the model has to choose between. A surface that grows without limit gets picked from badly.
The last one earns its slot by returning more than a tool list. For each tool it gives the tier required and the scopes required, plus the spec version and the output shape. It also states that those gates are the spec and the real check happens at call time against your key, which is what an agent needs before it plans three calls it cannot make.
What is the free tier, exactly?
A key is the string intelligence_ followed by thirty two url safe random characters. Only its SHA-256 hash is stored. The first sixteen characters are kept as a display prefix, so you can tell two keys apart in a list without the list being worth stealing.
Free is one thousand requests a month, at sixty a minute, reading the same dataset every paid tier reads. It needs an account and a key, because the monthly limit is metered per user and an anonymous caller cannot be metered. It needs no card. There is no clock on it.
Pro is 99 dollars a month. It adds throughput, full history, and the lineage endpoint that tells you where a figure came from field by field. Ultra is 199 dollars a month, and adds the company leveling catalog and the annual reports. Both sit on the pricing page.
One thing is not open yet. Signup on production is an early access waitlist across all three Orbyt products, because live payments are not wired up. The docs, the specs and the demo endpoint are public in the meantime, which is the right order anyway. Read the contract first.
How many engines actually serve data?
Two. The product page says so in a heading, which was not a comfortable sentence to publish. Role Taxonomy and Skill Premiums serve. Four more are designed, specified, and have pages written about them.
Designed and documented is not serving.
I know the shape of that lie because I shipped it. The US ingestion connectors for BLS and H-1B had parse logic that passed every fixture test, and the step that fetches the file had never been written. Not one row moved. Each source now reads from an operator set URL, and a source that cannot be fetched lands in the run result as a failed source rather than passing quietly. The engines page says which engine is which.
What is the number, really?
A United States salary figure from this API is a computed estimate. A national role baseline, times a cost of living multiplier, times a deterministic factor, rounded.
That last factor needs a plain description, because the word deterministic is doing a lot of quiet work. It is a hash of the role slug and the city slug. It moves the figure by at most four parts in a hundred in either direction, and it carries no information about that market. It exists so a city figure is not the baseline times the multiplier exactly. Which means a small gap between two roles in one city, or one role across two cities, is not evidence of anything.
The catalog covers 3,445 roles, and 513 distinct bands serve them, where a band is one low, median and high triple. So most roles share a band with another role. A title does not get its own measured number, and the data catalog discloses that rather than implying otherwise. Both counts come back out of the served catalog by running npm run check:salary-band-collisions.
The published wage data held here is BLS Occupational Employment and Wage Statistics, and Department of Labor H-1B filings, which record a filed minimum visa base wage rather than market pay. Where BLS publishes a wage for the occupation a role maps to, it is carried beside the estimate. Labelled with its occupation code and metro area.
Beside, not inside.
There is a reconciliation engine here that does blend sources by weight, and it runs over ingested measurements. The served US estimate is not one of its outputs. That is exactly why the estimate's own source breakdown reads a single entry at 1.0 rather than a weighted mix.
Two sources that used to be named in that response have never contributed a row. I had been naming four sources in prose without ever counting the rows behind two of them, while the structured field two lines above that prose had been saying computed the whole time. Counting against production took an afternoon. The list is now the two that can be traced.
Where do you read the contract?
The developers hub covers both APIs. This one, and the Orbyt API, which lets you build against your own Jobs data with a bearer token. OpenAPI specs for both are public files, alongside machine readable MCP manifests and a trimmed spec built for GPT Actions.
Related reading:
- The methodology page how a served figure is computed, and what it is not
- Intelligence pricing what free, Pro and Ultra each actually include
- The engines page which two engines serve data and which four do not
- The build blog more of these, including the posts about what broke
Methodology
Every number in this table I read out of something anyone can open, not out of a note to myself. The role count and the band count come from the published role dataset file at /data/salary-roles-all.json: I downloaded it, counted the records, then counted the distinct low, median and high triples across them, and checked both counts against the repository guard npm run check:salary-band-collisions, which prints the same pair. The sample size I got by calling the keyless demo endpoint and reading estimate.sample_size straight out of the response.
Limitations
A band count is a fact about the catalogue, not about the market. It says 3,445 titles resolve to 513 distinct low, median and high triples, so most titles do not carry a figure measured for that title. It says nothing about whether any of those figures is close to what a person in that job is actually paid, because a served United States figure here is a computed estimate rather than a published wage, and no band in that file has ever been checked against a real offer. The zero in the third row is the demo response's own value, which is a sample and labels itself one. The wider claim in the body, that a live served estimate reports zero as well, I read in the route handler of a repository that is not public, so that is my word rather than a link you can follow.
Sources
Common questions
What does the Intelligence free tier include?
One thousand requests a month at sixty a minute, reading the same dataset every paid tier reads. It needs an account and an API key, because the monthly limit is metered per user and an anonymous caller cannot be metered. It needs no card. The free tier is the evaluation path, so there is no clock on it.
What is the difference between the REST API and the MCP server?
Same data, two shapes. REST returns the locked response envelope for code you write: a data body, a request id, structured errors. MCP speaks JSON-RPC over a single endpoint and returns a decision ready object built for an agent, with a quotable answer sentence, a citation, and stated limitations.
Are the salary figures published government wages?
No. A served United States figure is a computed estimate: a national role baseline, a cost of living multiplier, and a hash of the role and city slugs that moves it slightly, rounded. Its sample size is reported as zero and its source breakdown reads one entry. The BLS and H-1B wage data held here is carried beside the estimate, never inside it.
Why does every response carry a request id?
Because support without one is guesswork. Every response carries a request id, success or error, in the body and in a header. The same value propagates into MCP citations. It is the key for the log explorer and the tag on the trace. Somebody can hand me one string and I can find the call.
Can I start using it today?
Not on production. Signup across all three Orbyt products is an early access waitlist while live payments are still being wired up. The docs, the specs and the demo endpoint are all public in the meantime, so you can read the exact shape of a response before you ever hold a key.
Related research
Follow the work.
I write these while the Machine runs. Get the next one wherever you already read.




