Skip to content

2026-09-09

Three fixes: partial bulk results, monitor baselines and key spend caps

A request log you can search by request id. Search everything with Cmd+K. Business verification and cross-signal consistency get a dashboard. Every API failure now answers in the documented envelope, including 404, 405 and 500. A bulk run that fails part-way keeps its billed rows downloadable and a run that produced nothing answers the new 409 JOB_FAILED, a monitor stopped on credits no longer loses the baseline for the entries it did not reach, and a key's daily credit cap now stops a batch mid-list with meta.stopped_reason key_spend_cap.

  • A failed bulk run's results are downloadable. A run that fails part-way keeps the rows it did write, and those rows were billed, so they now download exactly like the rows of a run you cancelled part-way. Until today they were unreachable: work you had paid for was thrown away with the run that failed.
  • A run that failed before writing anything answers a new 409 JOB_FAILED — see /docs/errors/JOB_FAILED. It used to answer RESULTS_NOT_READY, which asks you to poll the job until it reports completed, for a job that had already stopped and never would. Nothing was billed for a run like that.
  • A monitor that runs out of credits no longer loses its place. Every monitor compares each entry against the previous run to decide whether to alert you. A run that stopped on credits used to discard the stored answers for everything it had not reached, so the next run read those entries as seen for the first time — and a first sighting only sets the baseline, it never emails. Whatever changed in between was never reported.
  • Worst case, on the business monitor: a company struck off the register read as unverifiable rather than left_register, and the single most important event a monitor exists to catch passed silently. The stored answers are now carried forward for every entry a stopped run did not reach, so the next run compares against the last real answer.
  • A key's daily credit cap now holds inside a batch. The cap was checked once, before the request ran, so a single batch could spend far past it — a cap of 10 did not hold. A batch now stops at the cap, keeps everything it has already paid for, and answers 200 with meta.stopped_reason: "key_spend_cap" beside the existing "insufficient_credits"; those two remain the only values.
  • A call that starts with the cap already spent still answers 429 KEY_SPEND_CAP_REACHED and runs nothing, as before. Bulk runs and monitor runs bill in the background, are never counted against a key, and are deliberately unaffected by the cap once they are under way.
  • Every API failure now answers in the documented shape. The reference has always promised one envelope — success: false with a code, a message and a request_id — but only four failures kept that promise. A wrong job or monitor id answered a raw framework message that named an internal class, carried no code to branch on and no request id to quote at support; a wrong HTTP verb and an unhandled failure did the same. Those now answer 404 NOT_FOUND, 405 METHOD_NOT_ALLOWED (with an Allow header) and 500 SERVER_ERROR in the same envelope as everything else, each with its own reference page.
  • A record belonging to another account answers exactly as one that never existed, deliberately: telling the two apart would confirm that somebody else holds that id. And on a 500 alone we say plainly that a failure landing after the lookup ran leaves the credit spent — every other failure bills nothing.
  • The MCP server's nine tools are listed everywhere again. verify-entity and check-consistency shipped yesterday and the pages that list the tools were not updated with them: the documentation said seven, the integrations page said four, and the registry entry named three of the five products. All four now agree, and a test pins them to what the server actually registers.
  • A dataset that stops refreshing now raises an alarm. Only the phone feeds warned when a sync failed; the other eleven could fail every night and nobody would learn until an answer was wrong. A daily sweep now reads the same list the status page publishes and reports any dataset that is missing or older than its own refresh cadence allows, naming the command that fixes it. It checks the age of the data rather than the success of a job on purpose: if the scheduler itself stops, no job runs to report anything, and that is the failure that matters most.
  • Failed completion webhooks now record what came back — the HTTP status your endpoint answered with, or the transport failure when it answered at all — so a wrong URL, an expired credential and a fault at your end are no longer the same word. The response body is never stored.
  • You get told before the balance runs out, and when a card fails. Until now an empty balance was something you discovered as a 402 in your own traffic, and a failed subscription payment was not surfaced at all. There is now a warning email as the balance approaches the line, an amber banner in the dashboard while it is under it, and an email when a payment fails saying what happens next — your credits and keys are untouched while the card is retried. The warning is sent at most once a day, and again if the balance climbs back over the line and falls a second time.
  • Uploading a file larger than the per-run limit used to report the truncated number as though it were the whole file. It now says how many rows were left out.
  • A monitor run that fails now says so. The scheduler moves a monitor's next run forward before it starts, so a slow one can never be run twice and billed twice — but that meant a run which died had already moved the schedule on, and left the previous run's figures on screen. The monitor looked healthy while it had skipped a cycle. A failed run is now recorded and shown as such, the saved answers it never got to are kept, and the panels name the reason a run stopped rather than assuming it was credits.
  • Entity monitors were rate-limited ten times more tightly than the other four products — their scope allowed 50 requests a second and the routes were getting 5, because one pattern was missing a wildcard the other three had.
  • Deleting your account now really removes your files. Every table belonging to an account already went with it, which made the deletion look complete — but a database cascade fires none of the application's own cleanup, so each bulk run's uploaded list and result file stayed on disk, orphaned, with no record left pointing at them. Those files are the addresses and numbers you sent us; the table rows never were. They are now deleted with the account, and any live subscription is cancelled at the same time so a closed account stops being charged.
  • The request id now leads somewhere. Every response has always carried an X-Request-Id header and repeated it in the error envelope, and until today that id led nowhere — you could quote it to support and nothing else. There is now a Requests page listing your own API calls, opening on the ones that were refused or failed, with the typed error code beside each and a box to paste an id into. GET /api/v1/account/requests/{id} reads the same row back, so a script that catches a failure can ask what happened without a human involved. Kept for 30 days.
  • The log holds no value you looked up. It names the endpoint, never the address, number or identifier the call was about — the promise that a phone or IP lookup writes no history row still stands, and the privacy policy describes this store rather than leaving you to find it.
  • Search, on every page. Press Cmd+K, or Ctrl+K, or the search button in the header. It covers everything published here — the documentation, every API endpoint, every reason and error code, the guides, the free tools, the integrations, the comparisons and each changelog entry — just under three hundred pages that until now could only be reached by knowing the URL. The whole index is fetched once and searched in the browser, so it answers as fast as you can type.
  • Business verification and cross-signal consistency have a dashboard. Both shipped with an API, docs, an MCP tool and SDK methods, and no way to try either without writing code — the API-key form even offered scopes for products the dashboard could not exercise. /verify/entity reads an identifier against the registers held here and carries a Monitors tab; /verify/consistency runs any of the four signals together and shows where they agree and where they do not, quoting both sides of every comparison. Both are in the signed-in navigation.
  • The consistency page states what it will not do: no score, no risk level, no verdict, no recommendation. A difference between two fields is a fact about the fields, not a judgement about a person — and where the IP is a VPN or datacenter exit the country comparisons are withheld and say why, because that country belongs to the network operator.
  • Business verification and cross-signal consistency have product pages. Both were sold only through their API docs. The consistency page leads with what it refuses to do, because that is the product: no score, no risk level, no verdict and no recommendation, and there will not be one. The email product page also caught up with the other three — it now publishes the same measured latency and the same table of the datasets behind an answer.
  • The site now sells what shipped. The landing page still led with email verification alone, its heading counted four endpoints, and a footnote said email, phone and IP were "live today" — directly beneath a card selling the fourth product, with a fifth and sixth already live. The hero, the heading, the search-engine description and the structured data now describe all six, business verification and cross-signal consistency have cards and footer links, and a test keeps the page from falling behind the next launch.
  • Pricing says what each signal costs. /pricing described email verdicts only, for a product billing six signals off one balance. It now carries a per-signal rate card — what earns a credit and what is always free, for each endpoint — and states plainly that the two optional premium checks are off until a licensed partner is configured, so they currently add nothing.
  • A page shared or crawled while the renderer is down now carries its own title. Every public page's title, description and canonical are written by the server-side renderer. When that renderer is not answering the page still works — the browser draws it — but the HTML that arrives carries only the site name, so a link posted to Slack, an AI assistant reading the page, and a crawler that does not run scripts all saw "SPAW.co" and one sentence, whatever page it was. The HTML now falls back to the page's own title and description, taken from the same list the sitemap and the search box are built from, so the two can never disagree. A rendered page is unaffected: its head still wins, and no page ever carries two.
  • /status now reports whether the renderer is answering, because nothing else did: the site stayed up and every link preview quietly went generic. It is listed on the status page as its own component and is in the sitemap.
  • The address page publishes the weekly index check. Every Saturday, rows are taken straight out of each installed national register's own file and sent back through the ordinary lookup. It is a check on our own plumbing — a parser that spells a street one way and an index that folds it another would answer nothing for a whole country, silently — and the result is now on /products/address-verification with the share of rows found again for each register. It is deliberately not called coverage: it says nothing about how many of a country's addresses a register carries, and we make no claim there.
  • The site navigates like an app again. Every link in the header — the logo, Docs, Pricing, Guides, and the eleven signed-in tabs — was a plain anchor, so each click tore down the page and downloaded the whole front end again, while the footer had been navigating properly all along. They now navigate in place and fetch the next page while the pointer is still on the link, so most clicks land on a page already in hand. The docs sidebar does the same.
  • A first-run dashboard. The dashboard rendered exactly the same thing to an account that had just signed up and one running in production. A new account now sees what is left to do — create a key, make the first call — and the checklist disappears for good once that call lands.
  • The footer is a way around the site. It now carries the reason reference, the error codes, the MCP server, both OpenAPI formats and llms-full.txt alongside the docs, in place of a link to a scroll position on the front page. There is a live service-status pill beside the logo, and the twelve-line block of data licences — which we are obliged to publish and will keep publishing in full — is behind a "Data sources and licences" disclosure instead of outweighing the navigation above it. The signed-in pages, which had a copyright line and one email address, now carry the same way out.
  • Failures look like the rest of the site, and carry the id. A 500, a 503, an expired form and a rate-limit refusal all rendered the framework's own grey page. Each now has a page of its own that says what happened, whether anything was billed, and what to do next — and the 500 prints the request id, so someone hitting it in a browser can quote the same number an API caller reads out of the error envelope.
  • Confirmations reach every page. The component that draws a confirmation was mounted only inside the signed-in shell, so a message flashed to the front page, the docs, the pricing page or a tool was thrown away silently. Deleting your account was the worst case: it worked, signed you out, and said nothing at all. It now confirms.
  • Keyboard and screen-reader gaps. Every page now opens with a skip link, so a keyboard user is not walked through the logo, five nav links and eleven tabs before reaching the page. The mobile menu closes on Escape and hands focus back, and closes when you navigate instead of covering the page you just opened. The signed-in tab strip scrolls the tab you are on into view. The theme switcher announces as one setting with three values rather than three unrelated buttons.
  • A theme control for everyone. Light, dark and system have worked since day one, but the only switch was inside the account settings, behind sign-in and email verification — so a visitor reading the documentation could not override their operating system. The control is now in the header on every page.
  • Every Markdown twin (/docs/api.md and several hundred like it) now names the HTML page it is a twin of, so the two stop competing with each other in search. The docs twins and the About and Security pages also end with a link home, which the guides and comparisons already did.
  • A bulk run that stopped on credits can be resumed. Until today the only way out was to upload the file again: the run kept the list it was given, and nothing offered it back to you. Top up and press Resume, on any of the four products, and the run continues — it re-runs the list from the top, the rows it already verified come back as free repeats inside the seven-day window, and only the rows it never reached are charged. POST /api/v1/{product}/bulk/{id}/resume does the same from a script.
  • A run whose uploaded list has passed its retention window cannot be resumed and says so, rather than starting again with nothing. Neither can an IP run made in privacy mode: privacy keeps no record of the addresses checked, not even the repeat marker, so a second pass would charge for every row again — the run says that instead of quietly billing twice.
  • Usage answers what you spent. /usage listed credit movements and nothing else — there was no credits-spent figure anywhere in the product. It now filters by date, activity, channel and key, and summarises whatever you have narrowed to: spent, added, a per-activity breakdown and a bar per day. The figures cover the whole range you filtered, not the rows currently on screen, and the CSV download takes the same filters.
  • Spend is attributed to the exact key that made it. Ledger rows recorded a label — "api · Production" — which two keys of the same name share and a rename does not move. Each row now also carries the key's own id, so /usage can break spend down by key, GET /api/v1/account/ledger can be filtered to one with api_token_id, and the CSV carries the column too. A revoked key keeps its history under the name it was spending with. Work that runs in the background — bulk runs, monitors — is deliberately attributed to the run rather than to the key that scheduled it, and the table says so instead of leaving you to wonder why the numbers do not add up.
  • The API keys page showed "none" in the cap column for a key with no daily cap, throwing away the number of credits it had spent today — which the page below it was already showing for publishable keys. It now shows both.
  • The documentation says where you are again. The sidebar decided which entry was current by page, not by section, so on a page with ten anchors all ten lit up at once — a highlight that was not merely unhelpful but wrong. It now follows the section actually on screen. And on a phone, where the sidebar is hidden, a long reference page had no in-page navigation at all: there is now a row of the current page's sections above it.
  • Analytics now asks first. Google Analytics used to load on every page for everyone. It is not loaded at all until you say yes — nothing about a visit reaches Google before you answer — and the answer is remembered in your own browser, not on your account. The privacy policy says exactly that now, and carries a control to change your mind, because a policy that says you can and offers nowhere to do it is not a policy. Saying no changes nothing else about the site.
  • The privacy policy and the terms cover what we actually run. Both still described the product as email and domains "and — as they launch — phone numbers and IP addresses", while five signals and a cross-signal comparison were live. They now name all of them, and the privacy policy says plainly what each one keeps: a postal address lookup writes no history, but your suppression list and the delivery outcomes you report store the address as you wrote it rather than a hash, because both exist to be matched against a later lookup. A company lookup writes nothing at all and its answer never carries a street address — a registered address is very often a home address. Cross-signal consistency keeps nothing of its own: it writes whatever the signals it ran would have written, and the comparison itself is returned and never stored.
  • A bulk run tells you when it is over. Until now the only way to learn that a run had finished was to keep the tab open for its four-second poll: a 40,000-row list started before lunch reported to nobody. There is now an email — counts, spend and a link, never a row of your list. A run that stopped on credits or failed is always mailed, because those are the endings you have to act on; a clean run only when nobody could still plausibly be watching. Cancelling mails nobody: you were the one who cancelled it. And the dashboard now shows the runs still going, whoever on the account started them.
  • A run started from the dashboard can post to your endpoint when it finishes, which until now only a run started through the API could do. Webhooks are also refused outright when the URL resolves onto our own network — a customer-supplied URL fetched by our servers is the shape of a server-side request forgery, and the check runs at delivery rather than only when the URL is saved, because a name can be repointed afterwards.
  • Suppression lists can be searched and cleared by value. Removing one address from a 50,000-entry list meant paging through 500 requests to find its numeric id. All three lists now filter by value, source, reason and date, and DELETE /api/v1/{signal}/suppressions takes the values themselves and answers which were on the list. The search matches the way the entry was stored, so an address you wrote differently, a number in national form, or a different capitalisation all find the row they folded into.
  • A free IP lookup, and an index of every free tool. The IP checker was the only one of the five signals with no tool page, though the widget for it already existed. It is at /tools/ip-lookup, and /tools now lists all eight — from any one of them you can reach the others, which until now you could only do through the footer.
  • A browser can finally read the headers we tell it to read. There was no CORS policy, so the framework defaults applied: no exposed headers and no preflight cache. X-Request-Id — the id /docs/errors tells you to quote to support — was invisible to browser JavaScript, as were the rate-limit headers, Retry-After and Idempotent-Replayed. All six are exposed now, preflights are cacheable for a day (the form widget posts JSON, so it was paying a full extra round trip on every lookup a visitor typed), and the policy is written down on /security rather than left to be discovered.
  • Every release has its own link, and the changelog has a feed. A release could not be linked to a customer and there was no way to follow the product without checking the page. Each entry is now its own page with its own Markdown twin, and /changelog.atom is a real Atom feed a reader can subscribe to — advertised from the pages themselves, so a reader finds it without being told.
  • Monitors can post to your endpoint, and can be changed without being rebuilt. Mail was a monitor's only channel while bulk runs have posted webhooks for months; monitors now do too, signed the same way, and refused outright when the URL resolves onto our own network. And PATCH /api/v1/{signal}/monitors/{id} finally exists: adding one entry, renaming a list or changing its cadence meant deleting and recreating the monitor, which threw away the baseline every alert is judged against — a full cycle of silence at exactly the moment you were paying attention.
  • The Python, Node and PHP clients caught up with the API. They had fallen a long way behind: resuming a bulk run, removing suppressions by value, filtering a suppression list, the request log, api_token_id on the ledger, and the monitor webhooks and updates above were all reachable over HTTP and from none of the three. They are now, with the same surface in each. Two older gaps closed with them: the ledger's rows were being thrown away by the Python and Node clients before you ever saw them, and narrowing bulk IP results by risk level was documented and implemented but unreachable from every client.
  • A monitor now tells you which entries changed, not just how many. Three of the four panels stopped at the summary counts, so you could read that two addresses had decayed and never learn which two — while the API had been returning the per-entry map all along. Every monitor now expands to the last run's answer for each entry, worst first. The address monitor marks the entries that changed, because its run records that per entry; the email and phone monitors record only how many changed, so they show the status and do not guess.
  • Every answer names its sources on the page, not just in the raw response. "A verdict that names its sources" is the whole position, and on four of the five signals the line was only visible to somebody who opened "Show raw response" — the pages filtered it out of the field list and printed nothing. It is now on all five, in one wording.
  • A leaked API key is no longer an outage. The page told you to revoke the key and create another, which means every integration using it is down between the two. Rotate issues a replacement carrying the same scopes, cap, address allowlist and default country, and the old key keeps working for 24 hours so you can move each service across — or you can end it immediately with Revoke now. The pair shares one daily spend cap while both work, because a cap is a promise about your spend and not about a row.
  • New accounts start with more than the monthly grant, and get an email that says so. Confirming your address now adds a one-off credit bonus and sends a welcome message with your balance, a link to make a key and a request you can paste and run. The bonus is once per mailbox: closing an account and opening another with the same address does not pay again, which we keep track of as a one-way hash and nothing else — the privacy policy says so plainly.
  • Business identifiers can be checked in batches. Entity was the only signal with no batch endpoint. POST /v1/entity/batch takes up to 500 in one call and bills the way a single lookup does: only the ones that were found. An empty balance no longer abandons the identifiers that cost nothing.
  • Cross-signal consistency can compare a company. The endpoint that exists to cross-check signals could not cross-check the newest one. It now puts the registered name beside the organisation on the address, and the country of registration beside the country the address stands in — kept out of the overall country agreement on purpose, because where a company is registered is not where a person is. The name comparison is a normalised exact match and says so: accents, ampersands, apostrophes, initials and company-form words are taken out first, so a difference is a difference in the name rather than in how it was typed.
  • The request we print for your first call was missing a header, so pasting it answered "the email field is required". Fixed everywhere it appears.
  • What the API promises, written down. There was nothing anywhere saying what counts as a breaking change, what may appear without notice, or what would happen if something were deprecated — so an integration had to guess. /security now says it: the OpenAPI document is the contract and a test fails the build if it and the routes disagree; new fields, new enum values and new error codes ship without notice, so match on the codes you handle and let the rest fall through; a breaking change gets a new path rather than a quiet edit; nothing is deprecated today, and when something is it will carry a Sunset header and be announced in the changelog. The same page states plainly that there is no numeric uptime commitment — and points at the live measurement there is, including why a refused request is kept out of the latency percentiles.
  • A copy button on every code block. Around forty of them across the documentation, four generated samples per endpoint, and every block in the guides — all of them had to be selected by hand. The button leaves the shell prompt behind, so what you paste is a command that runs.
  • A privacy-forwarding address can open an account. Public burner lists carry the permanent alias services — Firefox Relay, Addy.io, SimpleLogin, DuckDuckGo Email Protection and the rest — and we were reading those lists at our own signup form, so a customer whose only address was a Firefox Relay or Addy alias was turned away at 422. Those are real mailboxes people keep for years, and for a privacy-minded customer they are often the only address on offer. They open an account now. What the API answers about those domains is deliberately unchanged: opening our own front door is not the same as changing the verdict you buy.
  • Knowing someone's email address no longer blocks their password reset. Asking for a reset link is limited by the address on purpose, so a botnet cannot fill one person's inbox from a thousand source addresses — but completing a reset shared that counter, so an attacker who knew nothing but a victim's address could spend the hourly allowance and leave the owner holding a valid link the form would no longer take. The two are separate now; completing a reset is limited by the source address, which is what the risk actually is.
  • Deleting your account deletes the devices it was signed in on. The confirmation says "your account and everything in it", and the sessions table — one row per browser, carrying the IP address and user agent each was last seen on, the same record the security page had just shown you — outlived the account by whatever was left of the session lifetime. Nothing else ever deleted those rows.
  • "Sign out everywhere else" is always offered. The button appeared only when the device list had rows in it. The list shows sessions that have been active recently, so a browser that chose to stay signed in and has not been used since drops off it and is still signed in — the button was being hidden in exactly the case somebody came to the page for. The dialog now says what it covers, including the devices no longer listed.
  • A malformed query parameter is a 422, not a 500. ?format[]=json on a bulk results download answered a server error — our fault, reported as something you could do nothing about — after the endpoint had already decided you were allowed to ask. It now answers the same validation error a misspelled value does.
  • format=json on a bulk download handles what CSV always could. A quoted cell holding a newline — a two-line address, a note — was being read as two rows, so the JSON came back with extra rows and the value cut in half while the CSV download of the same file was perfect. And a single byte that is not UTF-8, which an uploaded list routinely carries, raised mid-stream after the 200 and half the rows had already gone out. Both fixed, and a test now downloads one file both ways and requires them to agree.
  • A report you type into the dashboard stops disturbing everyone else's lookups. Dashboard reports are deliberately left out of the figures other accounts feel, and the code still threw away the shared cached count on every one of them — a count that could not have moved — making each click re-run the query behind everybody's lookups.
  • The line under the phone report buttons said repeated failures suppress a number, while one report of abuse suppressed it immediately. The button with the most consequence was the one described least accurately.
  • The privacy policy now covers the devices list and says what removes a row, and names the dashboard as the third way an outcome report reaches us alongside the API and your provider's bounce webhook — with the rule that separates it from the other two.
  • The three clients caught up again, with six operations. Listing your bulk runs in each of the four products (how a caller that lost a job id finds the run again), format=json on all four results downloads, the business-identifier batch, the regulator's number-range report, and the status endpoint — the one call that works before a key exists. Every batch item's index and input are documented and typed too: a batch stopped early answers only a prefix of the list, and the answer carries the normalised value, so those two are what line a result up with the row it came from.
  • A key's controls now hold over the MCP server. A secret key can carry a scope, a daily credit cap and an allowlist of addresses, and /mcp sat outside the middleware that enforces all three — so a key scoped to email alone, capped at ten credits and locked to one office address ran unrestricted lookups from anywhere as soon as an agent pointed at it. The documentation had been telling you the allowlist "makes a leaked key useless elsewhere". It does now. A refusal comes back as something the model can read and act on, not as a broken transport: an agent that asks for a product its key does not cover is told which scope it needs.
  • Agents can check a list. The MCP server had one batch tool, for email, and its input was literally named emails, so an agent checking two hundred phone numbers made two hundred calls. There are now batch tools for phone numbers, IP addresses and postal addresses on the same shape — twelve tools in all — and they bill and stop exactly as the REST endpoints do, keeping the rows already paid for when a balance runs out.
  • The form widget checks postal addresses. The browser endpoints for all four signals have been built and billed for weeks, and the widget on your own pages attached to email and phone inputs only — so the highest-value case, validating a shipping address at checkout, had no first-party widget at all. data-spaw-address now does it, taking its country from the field you name, and the spaw:result event says which of the three fired.
  • The widget waits for a country select to settle before it looks anything up. A keyboard user arrowing down a country list fires a change on every option they pass, and each is a different country, so the first version of this would have spent ten credits on nine countries nobody chose. And an answer for a country that has since been changed is now dropped rather than rendered: it described an address nobody was submitting.
  • Business identifiers reached the connectors. Zapier, n8n, Make and Google Sheets had every other signal — email, domain, phone, IP, address, suppressions and outcome reporting — and not this one, though all three SDK clients have covered it since it shipped. All four now verify a LEI or a company number, with the field lists, the caps and the caching each platform expects.
  • Those pages carry the caveat that matters more than anything else on them: the UK company file is a snapshot of the live register, so a struck-off company is removed from it rather than marked dissolved. A real dissolution arrives as found: false, not as the flag whose name suggests it — and on a LEI lookup it is quieter still, showing up as an empty agreements block while the register is still named. Filtering on the flag alone keeps you dealing with exactly the companies the feature exists to catch.
  • Business verification has a free tool. It was the only product with no way to try it without an account, though both registers are synced onto this server, so a public lookup is one database query with nobody to pay. /tools/company-lookup reads a company number or a LEI and answers the legal name, the register's own wording for its status, the jurisdiction and the incorporation date, each stamped with the date of the copy read. No registered address, deliberately — an identifier does not say whether a company or a sole trader stands behind it.
  • Every free tool now has its own rate budget, and so does every browser endpoint. All six tool demos shared one bucket per visitor, and so did the four widget endpoints, while each page and each documentation section promised its own number: checking an address and a phone number on one page quietly spent one allowance twice. Each is separate now, which is what makes the published number true. A test refuses any new guest-facing route that joins a shared bucket.
  • The endpoint reference shows the official clients. Every one of the API pages offered curl, fetch, requests and curl_setopt, and never spaw.verifyEmail(...), though all three clients cover every endpoint. All three now appear beside the four, generated from the same example the curl uses so they cannot drift apart — and the OpenAPI document you import into Postman, Scalar or Redocly carries them as x-codeSamples, where it previously arrived with a hundred example-free operations.
  • The samples are checked rather than trusted: a test fails if a sample names a method its client no longer declares, or passes a field the operation does not have, or quietly omits one the curl sample sends — which it was doing, dropping the address from the consistency call.
  • Smaller things: Billing has a place in the navigation and in the account menu instead of one link on the dashboard, the credit balance on the usage page is now the way to top it up, and the key-controls copy on /docs names all seven scopes rather than the four that existed when it was written.

markdown version: /changelog/2026-09-09-partial-results-monitor-baselines-and-spend-caps.md · atom feed: /changelog.atom

All releases