899 APIs Can Upsert — 39 Tell You What Happened

899 APIs Can Upsert — 39 Tell You What Happened

An upsert is one write that resolves an existing record by a key you supply and either updates it or creates it. Without one, every write to a CRM starts with a search and a branch: look for the contact, decide, then call create or update. Skip that check once and you have manufactured a duplicate in somebody’s database. It is the most ordinary integration chore there is, so we measured how much of the catalog has actually retired it. Of the 27,000-plus providers on APIs.io, 8,006 publish an OpenAPI119,154 specification files between them, sitting behind the API pages in the catalog. That subset is the whole measurable population here: a provider who documents an upsert in HTML and nowhere else cannot be counted either way.

The pattern is detectable because it leaves four separate marks in a contract, and they are worth separating. Naming is an operation called upsert or create_or_update. An alternate key is a field letting the caller name the identifier to match on — idProperty, external_id, id_type, match_on — which is the half that actually moves identity resolution to the server. A prose contract is the behavior existing only in a description, usually on a PUT /{id}, where no machine will ever find it. An outcome discriminator is the response telling you which branch ran.

The result

Signal Providers Share
Named upsert operation 788 9.8%
Alternate key on a write 598 7.5%
Behavior stated only in prose 179 2.2%
Upsert flag on an ordinary create 91 1.1%
Outcome discriminator in the response 55 0.7%
All of it together 63 0.8%

899 providers can create-or-update in a single call. 39 of them tell you which one happened.

HubSpot is the reference implementation and the only common CRM carrying every mark at once. Its batch upsert takes idProperty: email per input item and returns new: true or new: false per result, with 207 Multi-Status when part of a batch fails. You declare the key, it does the match, it hands back a receipt. Pipedrive is the counterexample, and a careful one: it has no upsert on persons, organizations or deals, yet its OpenAPI is full of the word — because it named its response schemas UpsertPersonResponse on the plain addPerson and updatePerson operations. Internal naming leaking into a public contract. Any detector matching on component titles rather than paths and operation IDs scores it as having the capability it conspicuously lacks.

Where vendors do ship it, they disagree entirely about where it belongs. Salesforce has encoded it in the URL since API version 46 — PATCH /sobjects/{OBJECT}/{FIELD_NAME}/{FIELD_VALUE} — with the external ID field as a path segment and no body flag at all. Zendesk chose a verb, POST /api/v2/organizations/create_or_update. Salesloft made it a resource, POST /person_upserts. Twenty adds no endpoint and hangs upsert: true off the ordinary create. Klaviyo takes external_id on profiles and catalog items without naming the operation at all.

The 0.7% is the number that matters, and it is an agent-readiness problem before it is a data-hygiene one. Send two thousand records at an endpoint that helpfully creates-or-updates, get a 200, and nothing in the response says whether you onboarded nineteen hundred customers or touched nineteen hundred existing ones. You cannot reconcile it, report on it, or trigger anything from it without going back and asking — the exact round trip the upsert was supposed to delete. It is the same argument that puts idempotency in the Kin Score agent-readiness layer: an agent that retries needs to know what its retry did, and an agent that writes needs to be able to say what it changed. Adding one boolean to a response body is close to free. Ten times more providers have shipped the operation than have shipped the receipt.

← CloudZero Ships Scopes That Update Themselves
Entity Sport Publishes Seven Ways to Handle a Rate Limit It Has Not Published →