The Competitor SDK Diff Teardown: Public Repos to a Dated Launch Brief
Diff a rival's API spec, SDKs, CLI, and IaC provider over 90 days, label every new surface, and hand over a brief where each claim links to a commit.
Key takeaways
- Monitoring a rival's GitHub org costs almost nothing: a published GH Archive example processed about 2 GB for one org-month, against a 1 TB monthly BigQuery free tier.
- oasdiff's default threshold leaves out draft and alpha endpoints, so a competitor spec diff has to be rerun with --stability-level draft or it will miss the beta-gated paths the read exists to find.
- Stripe publishes its gating ladder as separate npm dist-tags (private-preview, public-preview, latest), so the release channel settles Gated Beta versus Shipped without any inference.
- In the worked case, five beta-channel changelog lines collapsed to three real surfaces once follow-up methods were merged into their parent resource and a field rename was paired with its removal.
- No public source measures the lag from spec path to launch. Stripe's ProductCatalog.TrialOffer was still gaining methods in preview at least four months after it first appeared, so every launch window is an analyst judgement.
- In Refolk's index only 7 of 463 US competitive and market-intelligence profiles (1.5%) list Python, which is why code-level competitor reading is rare and worth defending.
This guide shows how to tell product and sales what a rival API company is about to ship. It uses only the files the rival publishes on GitHub: its API spec, generated SDKs, CLI, and infrastructure-as-code provider. It is for strategy teams and talent-intelligence analysts who need dated claims they can defend, not commit-velocity charts. One worked case runs through the whole guide, wrong turns included, and it ends in a brief where every line links to a commit.
What a competitor's public SDK repos reveal before launch
A competitor's generated repos show the shape of features that are already in its API spec but haven't been announced: new paths, new resources, new enum values, and the release channel each one sits in. They do not tell you when a feature will launch, or whether it works yet.
The method depends on codegen. Codegen (code generation) is a pipeline that turns one OpenAPI spec into SDKs, a CLI, and an infrastructure-as-code (IaC) provider. An OpenAPI spec is the machine-readable description of every endpoint, field, and enum in an API. An IaC provider, such as a Terraform provider, lets customers manage the vendor's resources as config files. Once a pipeline like this is running, nobody chooses by hand what goes public. Cloudflare's Terraform provider is generated from its spec, and at its v5 release it added 25% more resources and more than 200 additional data sources. Codegen removes the human filter that used to keep unfinished resources out of view.
Where a pre-launch feature shows up
- Release channelTag suffixes, dist-tags, preview API versions - who is allowed to use it
- IaC providerNew Terraform resources and data sources, generated per service
- CLINew command names - supporting evidence only
- Generated SDKsNew resource classes, methods, types, beta namespaces
- API specNew paths, enum values, stability markers - the source of every layer above
Which artifacts separate a gated beta from a launch
Different providers mark gating in different ways, but each of the markers below is documented publicly. Learn which ones your competitor uses before you start reading its diffs.
| Artifact | Where it shows | What it proves |
|---|---|---|
| -alpha or -beta.X suffix | Stripe SDK versions | Private vs public preview |
| npm dist-tag | private-preview, public-preview, latest | Channel, no inference needed |
| Beta header and namespace | anthropic-beta, client.beta | Opt-in beta access |
| x-stability extension | OpenAPI operations | Declared draft, alpha, beta, stable |
| Path version | v2alpha, v2beta | Channel baked into the URL |
| Free text in spec | "early access", "feature flag" | Gated; server may reject |
Anthropic's TypeScript SDK goes further. Its AnthropicBeta union type lists dated beta identifiers such as 'files-api-2025-04-14', so a new identifier in that type is a new opt-in feature. Stripe documents a third mechanism: some preview features need a name and version set in the Stripe-Version header, for example feature_beta=v3.
Each artifact can mislead. A beta header proves a feature is real enough to opt into. It does not prove the feature will ever leave beta. A spec path with no marker proves nothing until you know how this provider treats untagged operations (see the labels section).
CLIs need care. A new CLI command is worth logging, but I found no documented convention that a "preview" command group means the feature is gated. Use CLI evidence to support a label, never to decide one.
The four labels: Shipped, Gated Beta, Scaffolding, Noise
Every new surface gets exactly one label, and two questions decide it: which release channel it ships in, and whether public evidence shows the server accepts it. A surface is one thing a customer could call or configure: a path, a resource, a method, a field, or an enum value.
- Shipped. The surface is in the GA channel (for Stripe, the
latestdist-tag) and has no open issue showing the server rejects it. It proves customers can use it today. It can mislead when the spec runs ahead of the server, which the worked case shows. - Gated Beta. The surface sits behind a channel marker from the table above. It proves the feature is real and some customers can use it. It can mislead when the shape is still changing (a field rename) or when the surface is later withdrawn.
- Scaffolding. The surface is in the spec or the generated code, but nothing public shows the server accepts it, or it was removed before reaching GA. It proves intent and nothing more.
- Noise. Changes that come from regeneration itself: generated header comments, spec-hash bumps, documentation-only edits. They say nothing about the roadmap.
Labelling a new surface
Before you label anything, find out what an untagged operation means for this provider. oasdiff, the spec-diff tool used below, assumes an untagged endpoint is stable. Boston Dynamics' Orbit API docs say the opposite: "An operation with no tag should be assumed to be alpha." Apply one rule to both and you get opposite answers. Write down the provider's convention at the top of your working file.
The strongest single spec signal is a stability move. oasdiff reports both api-stability-increased and api-stability-decreased. A path moving from beta to stable is about as close as a spec gets to announcing a launch.
What does monitoring a rival's GitHub org cost?
In money, almost nothing. In a published GH Archive example, a query covering one month of one whole org processed 2 GB, and BigQuery gives you 1 TB of processing free each month. That leaves room for roughly 500 org-months (derived). The real limit is the rate limit on file-level API calls.
GH Archive is a public record of GitHub events going back to February 2011, available as a BigQuery dataset that updates hourly. It gives you every push, tag, and release for an org without cloning anything.
| Surface | Unit | Cost or limit |
|---|---|---|
| GH Archive, one day, one repo | 1 query | 157.69 MB |
| GH Archive, one month, one org | 1 query | 2 GB |
| BigQuery free tier | per month | 1 TB |
| REST API, authenticated | per hour | 5,000 requests |
| GraphQL API | per hour | 5,000 points |
The same examples also include a single repo-month query that processed 5.08 GB, more than the whole-org month. How you write a query matters more than how much it covers, so dry-run every query before you run it.
I could not verify whether GH Archive push events include the paths of the files changed, so plan as if they don't. That leaves file-level detection to GitHub's REST compare and commit endpoints. Unauthenticated calls get 60 requests per hour per IP. Authenticated calls get 5,000 per hour, with secondary limits of 900 points per minute for REST and 2,000 per minute for GraphQL. A GitHub App owned by a GitHub Enterprise Cloud organization gets 15,000 requests per hour. For a single competitor over 90 days, the authenticated limit is usually enough if you only fetch compares between release tags rather than every commit.
Who can run a code-level competitor read?
You need someone who can read a diff and run a command line, and most CI teams don't have that person. In Refolk's index of professional profiles, 463 US profiles hold competitive-intelligence or market-intelligence titles, and only 7 of them list Python as a skill.
| Market | CI/MI profiles | Top title in sample of 25 | Share of sample |
|---|---|---|---|
| United States | 463 | Market Intelligence Analyst | 44% |
| United Kingdom | 77 | Market Intelligence Analyst | 64% |
| US:UK ratio | 6.0x | - | - |
Counts come from Refolk's index, and the ratio and shares are derived. In the US sample, "Market Intelligence Analyst" (11 of 25) leads "Competitive Intelligence Analyst" (7 of 25). The pattern is consistent: the CI role is staffed from market research, not engineering, so reading code is rare in it. That rarity is why this method gives you an advantage. The tooling is free, and very few teams have someone who can use it.
You have three practical options. You can pair your analyst with an SDK engineer for the diff steps. You can train the analyst on the seven-step procedure below. Or you can hire someone who already sits at the overlap. Searching for that overlap by job title fails, because the skill rarely appears in titles. Refolk takes the request in plain English and searches LinkedIn, GitHub, and the open web for people who match it.
The 90-day diff, step by step
The procedure has seven steps, from repo inventory to brief. By my estimates, the first three take roughly two and a half to three and a half hours per competitor. The rest depends on how many surfaces make it through the noise filter.
Practitioners disagree on where to start. Spec-first readers start with oasdiff. Others start with SDK release notes, because a changelog like Stripe's is easier to read than a spec diff. I run both and check each against the other: a surface that shows up in only one of them deserves a closer look.
Competitor SDK diff over a 90-day window
- Inventory the codegen reposFind spec, SDK, CLI, and IaC repos by their generator fingerprints (.stats.yml, a "generated by Stainless" header, terraform-provider-<name> naming) and record the source spec URL for each. Budget about an hour.
- Pull 90 days of org activityDry-run, then run a GH Archive org query for the window, expecting roughly 6 GB from the published 2 GB org-month example. Done means commit SHAs and release tags per repo.
- Diff the spec snapshots twiceRun oasdiff changelog on the start and end snapshots, then rerun with --stability-level draft so draft and alpha endpoints appear. Done means added paths, enum values, and stability changes.
- Diff the generated SDK and IaC treesDiff only resource and service directories, such as internal/services/*/resource.go, and drop hash-only commits. Done means every new resource or type maps back to a spec path.
- Tag each surface by release channelCheck tag suffixes, dist-tags, preview API versions, beta headers, and path versions. Done means every surface carries a channel.
- Label every surface with a commitAssign Shipped, Gated Beta, Scaffolding, or Noise from channel plus public server evidence. Done means every label cites a commit or release tag.
- Estimate a launch window and write the briefMeasure lag from this competitor's own history and state each window as analyst judgement. Done means a dated brief with evidence links.
The worked case: reading Stripe's preview channel, with a Cloudflare IaC check
In the worked case, five beta-channel changelog lines turned out to be three real surfaces, and none of them earned Shipped. The IaC check added one Scaffolding item and showed how much of a generated provider's churn is noise.
Why these two repos
I chose Stripe because it publishes its gating ladder openly, so the channel step can be checked without guessing. My notes don't cover a Stripe IaC provider, so for that layer I read Cloudflare's provider, which is generated from Cloudflare's spec and has a documented failure. If you're following along with your own competitor, run both halves against that one competitor.
Inventory and window
The entry point was stripe-node's release notes, plus Stripe's SDK versioning page. At the end of the window, npm showed three channels as three dist-tags: 23.0.0 on latest, 23.1.0-beta.1 on public-preview, and 23.1.0-alpha.2 on private-preview. Stripe's docs say private preview features are available only after invitation, through SDK versions with an alpha suffix. Public preview uses -beta.X. Preview releases also pin a dated preview API version (format YYYY-MM-DD.preview). Both the dist-tags and the current preview version change between runs, so read them fresh from npm and the versioning page every time rather than reusing an old value.
The spec diff, and the first wrong turn
My first pass ran oasdiff at its default threshold. That is the most common wrong turn in this work. The default leaves out draft and alpha endpoints, which are exactly the ones a competitor read is looking for. The second run, with --stability-level draft, is the one that counts. I keep the default run too: anything that shows up only in the second run is pre-beta by definition.
# 1. Consumer-affecting changes at the default threshold (hides draft and alpha) oasdiff changelog spec-start.yaml spec-end.yaml # 2. Same diff including draft and alpha endpoints - the run that matters oasdiff changelog spec-start.yaml spec-end.yaml --stability-level draft # 3. Everything, including documentation-only edits (use to explain, never to count) oasdiff diff spec-start.yaml spec-end.yaml # 4. List generated files so you can exclude their header-only churn grep -rl "File generated from our OpenAPI spec by Stainless" . # 5. Diff only resource definitions in a generated Terraform provider git diff --stat START_SHA END_SHA -- 'internal/services/*/resource.go' # 6. Find commits that touched only the spec hash git log --format="%h %s" START_SHA..END_SHA -- .stats.yml
Swap in your snapshot filenames and the two commit SHAs that bound your 90-day window.
Release notes: five lines, three surfaces
These are the beta-channel lines I worked from, how I read them at first, and how I labelled them in the end.
| Beta-channel changelog line | First read | Final read |
|---|---|---|
| Add resource ProductCatalog.TrialOffer | New surface | Gated Beta |
| Add list and retrieve methods on TrialOffer | New surface | Same surface, maturing |
| Add tax_id on Checkout.Session.collected_information | New surface | Rename; shape still moving |
| Remove support for tax_ids | Removal | Other half of the rename |
| Remove payment_method_types on PaymentIntentConfirmParams | Removal | Withdrawn; not a launch |
The second wrong turn was counting tax_id as a new feature. It appeared in the same release as the removal of tax_ids, so it is one field being renamed, not one surface added and another dropped. The test is simple: pair every removal with any addition of a similar name in the same commit. The rename still matters, though. A field that is still being renamed is a long way from GA.
The payment_method_types removal came out of the beta channel. I log removals like this as withdrawn Scaffolding. They tell product what the competitor tried and then dropped. I couldn't establish how long that field had been present before it was removed.
Measuring lag on the one surface that allowed it
ProductCatalog.TrialOffer is the only surface where I could measure anything. It first appeared in stripe-node v21.1.0-beta.1, which pinned the preview API version 2026-03-25.preview. In v22.5.0-beta.1, pinned to 2026-07-29.preview, it was still gaining list and retrieve methods. That puts it at least four months in preview, and I can't verify when it reached GA. The fork here is whether to quote an industry lag figure. No such figure has been published, so I didn't quote one. The brief gives TrialOffer's window as an analyst judgement and cites both releases.
The IaC check, and the third wrong turn
Stainless generates schema.go, model.go, and resource.go for each service in Cloudflare's provider. Every regeneration rewrites the generated header comments and updates the spec hash in .stats.yml. My third wrong turn was counting regeneration commits as activity. Once I restricted the diff to new directories under internal/services/ and dropped commits that touched only the hash, the change list was down to resource definitions.
The documented trap in this provider is issue #4938. The spec described a feature as "an early access feature and gated behind a feature flag". The provider generated a model from that spec, and the server rejected it. The resource was in a GA provider, so a channel check alone would have called it Shipped. It is Scaffolding. Cloudflare's own changelog backs up the caution: it said v5 issues "currently impact about 15% of resources". Being in a GA provider is not proof that a resource works.
A new service directory also tells you which team built it, from the commit authors. If market sizing is part of the job, asking Refolk for engineers who have committed to cloudflare/terraform-provider-cloudflare on GitHub in the last year turns those commit handles into a map of the team behind the provider.
A spec path says what a rival intends to build. The release channel says who is allowed to use it.
How this read goes wrong: false launches and missed betas
Two kinds of mistake matter most: briefing something as launched when it is only Scaffolding, and missing beta-gated paths because the tools hid them. Each failure below is documented, and each has a check you can run.
Triage for one changed file
- Generated churn?Header comment or .stats.yml hash only - label Noise and stop
- Resource path?Keep only resource and service files mapped to a spec path
- Paired removal?Match against a similar-name addition in the same commit
- ChannelRead suffix, dist-tag, preview version, header, path version
- Server evidenceSearch open issues against the resource before Shipped
- The default stability threshold hides beta-gated paths. What it looks like: the spec diff shows nothing new while the beta SDK keeps gaining types. Check: rerun oasdiff with
--stability-level draft. - Untagged operations mean different things. What it looks like: Scaffolding labelled Shipped because the diff tool treated untagged paths as stable. Check: read the provider's own stability docs; Orbit treats untagged as alpha, oasdiff treats it as stable.
- A rename looks like a new surface plus a removal. What it looks like: tax_ids becoming tax_id is counted as one surface gained and one lost. Check: pair every removal with any addition of a similar name in the same commit.
- The spec runs ahead of the server. What it looks like: a generated resource the server rejects, as in Cloudflare #4938. Check: search open issues against the resource before you call it Shipped.
- Wrong channel. What it looks like: a surface from an -alpha release briefed as launched. Check: read the tag suffix and the dist-tag. Alpha means invite-only.
- Spec-hash churn. What it looks like: hundreds of changed files in a release with no new behaviour. Check: diff only resource and service paths and drop commits where only the hash changed.
- Ethics drift. What it looks like: someone calls a gated endpoint "just to confirm" it exists. Check: the read uses files only, and nobody authenticates against the competitor's API.
Where reading stops and access begins
The line is between reading files the competitor publishes and using access it hasn't given you. Reading a public repo is research. Calling a gated endpoint to see whether it responds is access, and no public CI code makes that acceptable.
SCIP, the competitive-intelligence professional body, requires members "to comply with all applicable laws, domestic and international." Its guidance says this means "avoiding activities such as hacking into competitors' systems, stealing trade secrets, or violating non-disclosure agreements." No CI code I found deals specifically with calling a competitor's gated endpoints. The closest documented boundary is Stripe's private preview, which is invite-only. If you weren't invited, the alpha channel is something to read about, not something to use. I haven't covered GitHub's terms of service or individual repo licences here, so check them with counsel before you automate collection at scale.
The launch window needs the same honesty. Nobody has published a median lag from spec path to launch, so every window in your brief is a judgement. Build the baseline from the competitor's own history instead: for each past surface that reached the GA channel, measure from its first alpha or beta release to its first latest tag. A handful of those measurements beats any number borrowed from elsewhere, and they let you show your reasoning when someone challenges the brief.
Handing over the brief and keeping it current
Each surface in the brief gets its own entry with a label, evidence links, a channel, and a dated window. Re-run the full diff every 90 days, and re-read the live channel markers on every run, because dist-tags and preview API versions change between runs.
SURFACE: <resource, path, method, field, or enum value> LABEL: <Shipped | Gated Beta | Scaffolding | Noise> FIRST SEEN: <release tag or commit link> on <date> LATEST EVIDENCE: <release tag or commit link> on <date> CHANNEL: <latest | public-preview (-beta) | private-preview (-alpha) | beta header | v2beta path | none> SERVER EVIDENCE: <public docs or changelog link | open issue link | none found> PAIRED CHANGES: <renames or removals merged into this entry, with links> LAUNCH WINDOW: <earliest date> to <latest date> - analyst judgement LAG BASIS: <past surfaces from this competitor used to set the window> WHAT WOULD CHANGE THIS: <the artifact that would move the label or window>
One entry per surface; delete the Window line for Noise, and keep withdrawn surfaces in a separate section.
On each 90-day run, check the brief against this list before you send it.
Before the brief goes to product and sales
- Every surface links to the commit or release tag where it first appeared.
- The spec diff was run with --stability-level draft as well as at the default threshold.
- The provider's rule for untagged operations is written at the top of the working file.
- Every removal was checked against similar-name additions in the same commit.
- Commits that changed only generated headers or the .stats.yml hash are excluded from all counts.
- Every Gated Beta cites a specific channel artifact: suffix, dist-tag, preview version, header, or path version.
- Every Shipped surface was checked for open issues showing the server rejects it.
- Dist-tags and the current preview API version were read fresh on this run, not copied from the last brief.
- Every launch window is marked as analyst judgement and names the lag history it rests on.
- Nobody on the team authenticated against the competitor's API to produce any line in the brief.
When a Gated Beta moves to latest, or oasdiff reports api-stability-increased on its path, send an update straight away rather than waiting for the next 90-day run. That transition is the launch signal product and sales asked for. Record its date as a new point in this competitor's lag history.
Questions practitioners ask
Is it legal to monitor a competitor's public GitHub repos for competitive intelligence?
Reading files a competitor chooses to publish is the activity professional codes leave open, as long as you comply with applicable law. SCIP asks members to obey all applicable laws and to avoid hacking competitors' systems, stealing trade secrets, or breaking NDAs. No public CI code covers calling a competitor's gated endpoints, so I treat that as off-limits. GitHub's terms and repo licences are not covered here, so check those with counsel.
How do I detect beta endpoints in a competitor's OpenAPI spec before launch?
Run oasdiff changelog between two spec snapshots twice: once at the default, and once with --stability-level draft, because the default leaves out draft and alpha endpoints. Then look for channel markers: x-stability values, v2beta or v2alpha path versions, beta headers, and free text such as early access or feature flag. Before you trust untagged paths, read the provider's docs. oasdiff treats untagged as stable, and some providers treat it as alpha.
How long after a beta SDK release does a feature usually launch?
Nobody has published a median, so don't quote one. Measure it for the competitor you're reading: for each surface that reached the GA channel, count the time from its first alpha or beta release to its first latest tag. The only partial anchor in my notes is Stripe's ProductCatalog.TrialOffer. It was still gaining methods in preview at least four months after it appeared, and its GA date is unverified.
Does GH Archive show which files changed in a commit?
I couldn't verify that it does, so plan as if it doesn't. Use GH Archive on BigQuery to get the commit SHAs and release tags for the whole org cheaply (about 2 GB per org-month in a published example). Then call GitHub's REST compare or commit endpoints for the file paths. Authenticated REST allows 5,000 requests per hour, with a secondary limit of 900 points per minute.
Does a new Terraform resource in a competitor's provider mean the feature is live?
No. A generated provider publishes whatever the spec contains. Cloudflare's provider generated a model from its spec that the server then rejected, for a feature the spec itself called early access and gated behind a feature flag. Cloudflare also said v5 issues affected about 15% of resources. Before you call a resource Shipped, search the open issues against it and check its release channel.
Which tool should I use to diff two OpenAPI specs from a competitor?
I use oasdiff. It separates consumer-affecting changes (changelog) from everything else, including documentation edits (diff). It also reports stability moves in both directions and claims to detect 773 distinct change types. Phil Sturgeon's write-up on breaking-change detection names openapi-changes and Redocly CLI as alternatives. Whichever tool you pick, check how it handles draft, alpha, and untagged endpoints before you trust its output.
Try it on the search you came here for
Stop building boolean strings. Just describe the person.
Type one sentence. I plan the search, read GitHub, public LinkedIn and Crunchbase records, and the open web as it is right now, and hand back a ranked list with the reason next to every name.
01Describe them
One plain sentence. Role, city, stack, stage, whatever matters to you.
02I read the web live
GitHub, public LinkedIn and Crunchbase records, the open web. Not a database that went stale last quarter.
03You read the shortlist
Ranked, with the reasoning under every name. Open a profile, ask a follow-up, narrow it down.
- Staff backend engineers in NYC who shipped Rust in production
- Series A fintechs in SF under 50 people, growing headcount this year
- Maintainers of fast-growing Rust web frameworks on GitHub
- No boolean, no filters, no seat to buy. One box.
- Read at search time, so a profile updated yesterday counts today.
- Every step visible as it runs, every name with its reason.
500 free credits on sign-up. No card, no demo call. See real searches.