Docs / Shared / Versioning & changes

Versioning & changes

What "v1 · stable" commits to, what counts as a breaking change, how much notice you get, and everything that has changed so far.

What “v1 · stable” commits to

Three things, and they are the whole promise:

What it does not commit to: the data. Sources change what they publish, stop publishing, and get taken down. That is the product, not a version question.

What counts as breaking

Breaking — you get noticeNot breaking — ships any time
Removing a field, or changing its typeAdding a field
Removing a parameter, or narrowing what it acceptsAdding a parameter
Changing what an existing parameter defaults toAdding an error code for a case that used to be a generic failure
Changing an HTTP status for an existing caseAdding an endpoint
Making a nullable field non-nullable, or the reverseChanging a message string (never branch on these — branch on code)

The line that catches people: a default is part of the contract. Changing what ?active= returns when you do not pass it changes every existing integration's result set without changing one line of their code, so it is treated as breaking even though nothing was removed.

Notice

90 days for anything in the left column, announced here and by email to every account with a key that has been used in the last 90 days. A removed field keeps being served, unchanged, for the whole window.

Three exceptions, and they are the only three:

If a change would break you and 90 days is not enough, say so — it is easier to hear that before the change than after.

Queued

Three breaking changes are planned and none of them has a date yet. They are listed here because you should not learn about them from an email.

ChangeEffectWhat to do now
active becomes nullable, and null means “no verdict” Today true is the default state and doubles as “we have not heard from this source in months”. Splitting those apart is the only way the field can be trusted. Postings from a source that has not run recently will read null instead of true. Handle null now. Treat it as its own state rather than falsy — or better, derive liveness yourself and stop depending on this field at all.
?active= on /v1/jobs moves from any to true Collections will exclude postings whose source has taken them down. Today they are included and carry "active": false. Pass active explicitly. active=any keeps today's behaviour forever; active=true gets tomorrow's now.
The birjob_source_name key is removed from facts Affects only postings imported from the archive that predates this API. The key repeats, in an old project's vocabulary, what source and source_name already say as first-class fields on every row. Read source or source_name instead. They are present on every posting, not just imported ones, and they already carry the same value. Nothing else in facts is changing.

A jobs API whose default includes withdrawn postings is answering the wrong question by default, which is why it is going to move. It has not moved yet because the retirement pass that decides active needs a quiet run of real traffic behind it first.

The first two are related and the order matters: defaulting the filter to true while true still means “no verdict” would make the default more misleading, not less — it would keep every posting from a dark source and drop only the ones we correctly retired. The nullable change comes first. The third is independent of both and can move on its own.

Neither is why active's documentation changed today. That was a correction, not a change: the field always behaved this way and the description overstated it. Nothing about the data moved.

Changelog

Everything that has changed on v1. Almost every entry is an addition. Two are not, and both say so where they appear: an unknown ?source= became a 400 on 2026-08-28, and ?country= was narrowed to refuse a code that is not one on 2026-09-05. Both fall under the third notice exception — a value neither could ever have matched — and neither withdrew a row anyone was receiving.

2026-09-15

2026-09-05

On the second entry and the 90 days. Narrowing what a parameter accepts is in the breaking column, and this shipped with no notice at all — two days after ?country= itself went live on 2026-09-03, not in the same release. The basis is the third exception rather than the calendar: UK and EN are not in the 249-code vocabulary the column is constrained to, so no posting has ever carried either and no key can ever have received a row under one. What a caller loses is an empty page and what it gains is the reason. Had the parameter been live for weeks the argument would be the same but the risk would not, because by then someone would plausibly be branching on that empty page — which is why it was done now rather than queued.

2026-08-31

The same customer as the entry below, on the next two things they hit. One was a real defect on our side and had been for as long as the field has existed.

2026-08-28

All of this came out of one customer's question: “I only use non-job-board vacancies, and most of them have no contact_email or description, so the real count is very small.” Two thirds of it turned out to be true, and the parts that were true were ours to fix.

2026-08-23

2026-08-22

2026-08-21

Before that

v1 has been serving jobs, events and quotes since March 2026. Changes before this log started were not tracked, which is the gap this page exists to close — everything from here is recorded.