Score your OpenAPI spec for SDK generation

Octri runs this audit on every spec it generates a client from. Paste a URL and get the same score out of 10, on the same fourteen rules, before anyone builds against your API. No account, and the spec is not kept.

JSON or YAML

An example audit runs alongside: a readiness score of 6.8 out of 10 across 24 operations, with the rules the spec passed and failed scrolling past it.

A number about the SDK, not about the API

The audit does not grade your API. It predicts what a code generator will be able to do with the document that describes it.

An API can be well designed, fast and widely used, and still describe itself in a way that produces a miserable client. The two are separate problems. A response with no declared schema is a method that returns any. An operation with no tag lands in a namespace named after a path segment. Two operations that resolve to the same public name, whatever their operationIds say, collide into one method, and the generator has to rename one of them behind your back.

So every rule below states what breaks in generated output rather than what offends a style guide. If a rule cannot name the consequence, it does not carry weight.

Deductions scale with how much of your surface a rule touches. One undocumented operation out of 500 costs almost nothing; a spec that documents none of them loses that rule’s full weight. A rule that fires at all costs a fifth of its weight straight away, because some failures matter at any scale.

8.0 and up

Strong

Generation gives you typed methods, named models and real doc comments. What is left is polish you can do after the first release.

6.0 to 7.9

Mixed

The client compiles and works, with soft spots your users will feel: a few untyped returns, some bare arguments, a namespace or two named after a path segment.

Under 6.0

Weak

Enough of the surface is undeclared that the generated client leaks the spec’s gaps into every caller. Fix the heavyweight rules before you publish an SDK from it.

Where the points actually go

Three rules can take 6 of the 10 between them. When a spec scores badly, it is almost always these.

The 14 rules by weight, heaviest first: 3 carry 2 points each, and the remaining 11 carry between 0.5 and 1.5. Every weight is listed in the table below.

Up to 2 points

Successful responses declare a schema

Without a response schema every method returns `unknown`/`any`, so callers get no types and no autocompletion.

Fix: Give the success response a schema, or a $ref to one in components. A 204 needs nothing.

Up to 2 points

Shared models live in components and are $ref'd

Inline object schemas become anonymous per-operation types. The same model inlined in 20 operations generates 20 near-identical types instead of one.

Fix: Move the shape into components.schemas once and $ref it from each operation that returns or accepts it.

Up to 2 points

Generated public names do not collide

When authored identifiers collapse to the same shared public name, the generator must silently rename one method; callers otherwise cannot predict the SDK surface from the spec.

Fix: Rename the authored operationId, or set that operation's SDK namespace. The audit proposes one and can apply it; your tags and the wire operation are left alone.

The fourteen rules, in full

All fourteen rules, heaviest first, with the weight each one can cost out of 10.

RuleWeightWhat it checksWhat it costs youWhat to do
Successful responses declare a schemaresponse-schema2 ptsEvery operation with a 2xx response other than 204 or 205 has a schema on that response.Without a response schema every method returns `unknown`/`any`, so callers get no types and no autocompletion.Give the success response a schema, or a $ref to one in components. A 204 needs nothing.
Shared models live in components and are $ref'dshared-schemas-referenced2 ptsThe same inline object shape, three properties or more, appearing in more than one operation.Inline object schemas become anonymous per-operation types. The same model inlined in 20 operations generates 20 near-identical types instead of one.Move the shape into components.schemas once and $ref it from each operation that returns or accepts it.
Generated public names do not collideoperation-id-unique2 ptsThe public name each operation resolves to, namespace and method together, checked for collisions across the whole document.When authored identifiers collapse to the same shared public name, the generator must silently rename one method; callers otherwise cannot predict the SDK surface from the spec.Rename the authored operationId, or set that operation's SDK namespace. The audit proposes one and can apply it; your tags and the wire operation are left alone.
Every path placeholder has a parameterpath-parameters-declared1.5 ptsEach `{placeholder}` in a path template has a matching `in: path` parameter on the operation or the path item.An undeclared `{id}` placeholder cannot become a typed method argument, so the generated request path is incomplete or uncompilable.Declare the parameter with `in: path`, `required: true`, and a type.
Every operation declares an operationIdoperation-id-present1 ptOperations with no operationId at all.Without one the generator synthesizes a name from the path, which changes whenever the path does. That is a silent breaking change for callers.Add an operationId. It pins the generated method name to something you control.
operationIds read as method namesoperation-id-naming1 ptoperationIds that repeat their own namespace or trail the HTTP verb, measured against the name the generator would suggest.The id becomes the method name. `ActionsActionsGet` generates `client.actions.actionsActionsGet()`; `getActions` generates `client.actions.get()`.Name the id for the action, not the route. The audit suggests one per operation and can apply it.
Operations carry a summary or descriptionoperation-documented1 ptOperations with neither a summary nor a description.Operation prose becomes the method's doc comment, the text an IDE shows on hover.Write one sentence of summary. It lands on the reference page and in the SDK's doc comment.
Request bodies are documentedrequest-body-documented1 ptRequest bodies that have a schema but no description on the body or on its fields.Body field descriptions become the doc comments on generated parameters; without them the SDK ships undocumented arguments.Describe the body, or the fields inside it. Field-level prose is the one that reaches the SDK's arguments.
Failure responses are declarederror-responses1 ptOperations declaring no 4xx, no 5xx and no `default` response.Declared error schemas become typed errors. Without them every failure surfaces as an untyped generic error.Declare the failures you actually return, or one `default` response carrying your error shape.
Operations are taggedoperation-tagged1 ptOperations with an empty or missing `tags` array.Tags drive namespacing. Untagged operations fall back to a path segment, producing lopsided or `misc` namespaces.Tag every operation. Tags are what decide whether a caller writes `client.invoices.list()` or `client.misc.list()`.
No duplicate or equivalent pathsduplicate-paths1 ptPaths that are identical once the parameter names are erased, such as `/users/{id}` beside `/users/{userId}`.Paths differing only in parameter NAME are the same route to a server; the generated SDK ends up with two methods that call the same endpoint.Keep one. A server routes both to the same handler, so the second is a duplicate method for your users.
Authentication is describedsecurity-declared1 ptWhether `components.securitySchemes` declares anything at all.Security schemes generate the SDK's auth configuration. A spec without them produces a client with no way to authenticate.Declare the scheme you use, such as an http bearer or an apiKey header, and reference it from the operations it guards.
Parameters explain their purposeparameters-documented0.75 ptsQuery, path, header and cookie parameters with a name and a location but no description.Parameter descriptions become IDE hints on generated method arguments; bare names force SDK users back to the API reference.Describe what the parameter does and what a valid value looks like. One line each.
A server URL is declaredserver-url0.5 ptsWhether the document declares at least one entry under `servers`.Without a server URL the generated client must ship with an empty or placeholder base URL, adding setup work and easy production misconfiguration.Add your production base URL under `servers`. Add the others too if you publish a sandbox.

One more finding sits outside the score. A spec past roughly 300 operations or 400 models gets packaging advice rather than a deduction, because the fix is structural: split the client by tag so a caller who uses three endpoints does not install all three hundred.

Specs you have heard of, scored

Public OpenAPI documents we fetched and scored against the same fourteen rules. Each one has its own page.

SpecScoreReadingOperationsLast scored
Box Platform APIraw.githubusercontent.com8.7Strong29717 Oct 2026
Adyen Checkout APIraw.githubusercontent.com8.7Strong282 Oct 2026
Twilio SendGrid Mail APIraw.githubusercontent.com8.6Strong314 Sept 2026
Swagger Petstore - OpenAPI 3.0petstore3.swagger.io8.3Strong198 Oct 2026
Slack Web APIraw.githubusercontent.com8.3Strong17414 Sept 2026
Asanaraw.githubusercontent.com8.2Strong25115 Oct 2026
The Plaid APIraw.githubusercontent.com7.8Mixed35016 Oct 2026
OpenAI APIraw.githubusercontent.com7.6Mixed36017 Oct 2026
Twilio - Apiraw.githubusercontent.com7.3Mixed1977 Oct 2026
GitHub v3 REST APIraw.githubusercontent.com6.9Mixed124217 Oct 2026
Stripe APIraw.githubusercontent.com6.6Mixed61230 Sept 2026

Each report is a reading of the document, not a grade of the API, and none of these vendors is affiliated with Octri. Scores are re-taken when the spec changes. To add your own, tick the publish box when you run the audit above.

What happens to a spec after it scores

The audit is the first thing Octri runs on a spec, and it keeps running.

Fix it in place

9 of the fourteen rules carry a button that writes the change into your spec and rescores it, so the number moves when you act on it.

Rechecked on every change

The score is re-taken whenever the document moves, which is what catches a new endpoint shipped without a response schema.

One fix, every surface

The same document generates the reference pages, the SDKs in ten languages and the MCP server, so one change reaches all of them.

Split by who feels it

Findings a docs reader feels are separated from findings only an SDK user feels, and each studio shows its own.

Questions

No. We keep the `info` block your spec declares, which is its title, version and contact, plus which rules deducted points. The document itself is read, scored and dropped. A URL you paste to a private staging spec does not become a copy on our side.