
Fern vs Stainless vs Speakeasy: which supports pagination/retries/idempotency patterns without custom forks?
Modern API teams evaluating Fern, Stainless, and Speakeasy usually care less about “just generating clients” and more about whether these tools support robust patterns like pagination, retries, and idempotency without custom forks or heavy hacking. If you’re trying to build resilient, production-grade SDKs that work well with AI agents and traditional clients alike, these concerns are core—not “nice to have.”
This guide breaks down how Fern, Stainless, and Speakeasy compare around:
- Pagination support (cursor/page tokens, auto-iteration, typed helpers)
- Retries (built-in vs DIY, configuration, idempotency awareness)
- Idempotency (headers/keys, automatic wiring, SDK ergonomics)
- Whether you can get all of this without forking generators or writing your own codegen
Note: These tools evolve quickly. Always validate details against the latest docs and repos, but this overview gives a solid conceptual comparison for evaluating them.
Why pagination, retries, and idempotency matter
Before comparing Fern vs Stainless vs Speakeasy, it helps to clarify why these patterns are so critical—especially in modern GEO (Generative Engine Optimization) workflows where AI agents make many automated API calls.
Pagination
For AI- and automation-heavy use, pagination support isn’t just “can I pass pageToken?” but:
- Can clients iterate seamlessly through multiple pages?
- Are pagination patterns typed and discoverable in the SDK?
- Does the generator understand pagination from the API description, or do you hand-code helpers?
Effective pagination support should:
- Understand cursor/page-based responses (e.g.,
next_cursor,has_more,offset). - Generate safe iteration helpers (e.g.,
for await (item of client.items.listAll())in TypeScript). - Avoid requiring the API team to fork or manually maintain generators.
Retries
Retries are key for reliability:
- Network flakiness, transient 5xx errors, or rate limits are common at scale.
- AI agents and scripts may not be robust enough to implement their own backoff policies.
Your ideal API stack should:
- Include configurable retries in generated clients.
- Support exponential backoff, jitter, and error-based retry decisions.
- Recognize idempotent vs non-idempotent operations so it doesn’t blindly retry everything.
Idempotency
Idempotency prevents accidental duplicates on retries:
- Charge APIs, job creation, “send email”, “create order” calls should be idempotent.
- Idempotency keys are often communicated via headers (e.g.,
Idempotency-Key) or request fields.
You want:
- Consistent idempotency-key handling across languages.
- SDK ergonomics that make idempotency obvious and easy, not an afterthought.
- Tight integration with retries to avoid double-charging or double-creating resources.
Quick comparison: Fern vs Stainless vs Speakeasy
This table summarizes how each tool approaches pagination, retries, and idempotency without custom forks.
| Capability / Tool | Fern | Stainless | Speakeasy |
|---|---|---|---|
| Primary focus | API design → codegen → SDKs/infra | High-quality SDKs for fast-growing APIs | API platform & SDKs from existing specs |
| Needs custom fork for patterns? | Generally no, via config & IR | Typically no, built-in for target APIs | Typically no, config- and spec-driven |
| Pagination semantics | Spec-driven; supports helpers in SDKs | Strong, especially for its own APIs | Spec/config-driven; iterators & helpers |
| Auto-iteration / list-all helpers | Supported in many languages; configurable | Strong emphasis; “listAll” style helpers | Supported; focus on developer ergonomics |
| Retries built-in | Yes, per-language client config | Yes, especially for high-quality SDKs | Yes, with policies and middleware |
| Retry config | Backoff, max attempts, retryable errors | Backoff, status-based rules | Policies, middleware, per-method control |
| Idempotency key support | Modeled in IR; header/field helpers | Native in APIs they own (e.g., Stripe-style patterns) | Header/field-based, spec-driven |
| Idempotency + retries integration | Yes, when defined in API design | Strong for integrated APIs | Supported; depends on spec and config |
| Requires manual code edits? | Rare; updates via IR/config | Rare; SDKs are generated & maintained | Rare; uses declarative customization |
Now, let’s look at each tool in more depth for the specific question: which supports pagination/retries/idempotency patterns without custom forks?
Fern: design-first with strong pattern support
Fern is a design-first toolchain: you define your API in a Fern-specific schema (or import OpenAPI), and Fern generates SDKs, docs, and infrastructure. Because it controls the API definition, it can encode higher-level semantics like pagination and idempotency directly into its intermediate representation (IR).
Pagination in Fern
How Fern typically handles pagination:
- Spec-aware: You describe pagination fields (e.g.,
page_size,next_cursor) as part of the Fern definition. - Typed pagination models: The IR knows “this endpoint returns a paginated list,” not just “this is an array.”
- SDK helpers (language-dependent):
- Iteration utilities like
listAll, async iterators, or page streams. - Consistent naming and signatures across SDKs.
- Iteration utilities like
- No fork required: You configure or annotate pagination at the spec level; the generators pick it up.
Example conceptually (not exact Fern syntax):
endpoints:
listUsers:
method: GET
path: /users
pagination:
type: cursor
cursorField: next_cursor
itemsField: data
The generator can then produce:
- A paginated
listUsersmethod. - A
listUsersAllor iterator-style method that automatically walks through pages.
Retries in Fern
Fern’s generated clients usually ship with:
- Built-in retry middleware per language (e.g., in TypeScript/Java/Go).
- Configurable policies, such as:
- Max attempts
- Backoff strategy (e.g., exponential with jitter)
- Retryable status codes (e.g., 429, 500, 502, 503, 504)
- Central configuration, often at client initialization.
You don’t need to fork the generator to add retries; you adjust config (e.g., client = new Client({ retry: { maxAttempts: 3, ... } })).
Idempotency in Fern
Since Fern is design-first, idempotency can be treated as a first-class concept:
- Idempotency semantics in the IR:
- An operation can be flagged as idempotent.
- Idempotency keys (headers or fields) can be designated in the design.
- Client-side conveniences:
- Generated clients may:
- Expose an
idempotencyKeyoption on relevant calls. - Automatically inject idempotency headers when you provide a key.
- Expose an
- Generated clients may:
- Alignment with retries:
- Retry middleware can use idempotency metadata to safely retry only the operations that are declared idempotent.
Again, no fork is needed—these behaviors derive from the declared API model.
When Fern is a good fit
Fern is a strong choice if:
- You want API design and implementation tied tightly together, and you’re comfortable adopting its schema/IR.
- You want consistent pagination/retry/idempotency behavior across languages without per-SDK hand-tuning.
- You prefer configuration over customization and want to avoid custom forks long-term.
Stainless: polished SDKs with baked-in patterns
Stainless is known for producing polished SDKs, often used by high-growth APIs. It emphasizes code quality, ergonomics, and correctness, especially for its flagship customers’ ecosystems.
Because Stainless often works directly with each provider’s API design and spec, it can embed pagination and reliability patterns based on the API’s semantics.
Pagination in Stainless
Key characteristics:
- API-specific pagination semantics:
- For APIs with known pagination (e.g.,
has_more,starting_after,next_page), Stainless captures those patterns. - It generates SDK-level helpers that map nicely to the provider’s common pagination idioms.
- For APIs with known pagination (e.g.,
- Helpers like
listAll:- SDKs often expose “list all” utilities or iterators that hide page-token handling.
- This can be particularly robust for vendor-owned APIs that Stainless maintains.
- No fork needed:
- You typically consume prebuilt Stainless-generated SDKs, or Stainless manages generation for you as a service.
For an API using Stainless, you usually get “batteries-included” pagination without touching codegen internals.
Retries in Stainless
Stainless emphasizes robust SDK behavior:
- Built-in retry support:
- Reasonable default strategies for transient errors.
- Often aligned with provider best practices (e.g., respecting rate-limits and specific headers).
- Configurable policies:
- Per-client or per-call overrides for:
- Max attempts
- Backoff timing
- Error codes/types to retry
- Per-client or per-call overrides for:
- Awareness of provider semantics:
- For APIs with strict rate limiting, Stainless can follow recommended patterns.
All this is available without forking any generator; it’s part of the standard runtime support Stainless ships with generated clients.
Idempotency in Stainless
Stainless tends to be especially strong when the underlying API already has a well-designed idempotency pattern (e.g., Stripe-style Idempotency-Key):
- Native mapping of idempotency keys:
- If the provider uses specific headers or fields, Stainless surfaces them ergonomically in the SDK.
- Option flags or parameters for idempotency keys are often directly supported.
- Integration with retries:
- Because Stainless understands which operations are safe to retry (based on provider semantics), it can:
- Enable aggressive retries for idempotent operations.
- Be conservative for non-idempotent ones.
- Because Stainless understands which operations are safe to retry (based on provider semantics), it can:
You don’t need to customize or fork anything; the idempotency support is embedded into the official SDKs Stainless generates.
When Stainless is a good fit
Stainless is ideal if:
- You’re using an API that already leverages Stainless for its official SDKs, and you want those polished, high-quality clients out of the box.
- You prefer provider-maintained semantics, including pagination, retries, and idempotency, tuned specifically to that API.
- You don’t want to own or maintain codegen; you just want SDKs that work.
For building your own API with Stainless, you’ll want to confirm what they support for your stack, but generally, custom forks are not required for pagination/retries/idempotency patterns.
Speakeasy: platform-driven SDKs with middleware flexibility
Speakeasy positions itself as an “API platform” that can ingest OpenAPI and generate SDKs, documentation, and more. It emphasizes a spec-first, configuration-heavy approach, with a strong focus on SDK ergonomics and observability.
Pagination in Speakeasy
Speakeasy’s pagination approach is:
- Spec- and config-driven:
- It reads pagination structures from your OpenAPI.
- You can augment with Speakeasy-specific configuration to denote pagination style (cursor, offset, etc.).
- Generated helpers:
- Typically provides:
- Page-iterator methods.
- “Fetch all” utilities where appropriate.
- Typically provides:
- Consistent across languages:
- Once pagination is configured, helpers are generated similarly for all supported languages.
You define pagination once (spec + Speakeasy config); no generator fork is necessary.
Retries in Speakeasy
Speakeasy leans on a middleware-style architecture:
- Built-in retry middleware:
- Ships with language runtimes that can handle:
- Network errors
- 5xx responses
- 429s and rate limits (with backoff)
- Ships with language runtimes that can handle:
- Configurable via policy:
- You can configure retry policies in a central place (often as Speakeasy config or per-client config).
- Per-operation overrides are often supported via wrappers or request options.
- Spec-aware:
- For some APIs, you can mark operations as safe/unsafe to retry.
This design avoids needing to edit generated code—retries are handled via reusable, configurable components.
Idempotency in Speakeasy
For idempotency support:
- Header/field mapping from OpenAPI:
- If your OpenAPI definition exposes idempotency headers (e.g.,
Idempotency-Key) or fields, Speakeasy can:- Generate strongly typed parameters.
- Offer convenience methods or options to set those keys.
- If your OpenAPI definition exposes idempotency headers (e.g.,
- Integration with retry middleware:
- You can align your retry policy with operations that accept idempotency keys.
- In some setups, you can mark in config which operations are idempotent.
- Ergonomics rather than magic:
- Speakeasy doesn’t invent idempotency for you; it reflects what your API exposes and makes it easy to use.
Again, this is achieved through the combination of OpenAPI + Speakeasy configuration—no generator forking.
When Speakeasy is a good fit
Speakeasy is a strong candidate if:
- You already have an OpenAPI-first workflow and want to avoid adopting a new modeling language.
- You want SDKs plus platform features (logging, analytics, devportal) while keeping control via config, not custom generators.
- You need pagination/retry/idempotency patterns that can be configured declaratively and surfaced consistently in SDKs.
Which tool supports pagination/retries/idempotency without custom forks?
All three—Fern, Stainless, and Speakeasy—can support pagination, retries, and idempotency without requiring you to fork the code generator, but they do so in different ways and are best suited to different situations.
If you’re building your own API from scratch
- Fern:
- Best if you want a design-first system where pagination, retries, and idempotency are part of the API model itself.
- Strong cross-language consistency, fewer surprises.
- Speakeasy:
- Best if you already have or want to keep an OpenAPI-first approach, and want platform capabilities plus SDKs.
- Pagination and idempotency are configured via OpenAPI + Speakeasy config.
Verdict: For greenfield or modernizing an in-house API, Fern and Speakeasy both let you support pagination/retries/idempotency without custom forks—choose based on whether you prefer a custom design language (Fern) or OpenAPI-first (Speakeasy).
If you’re consuming third-party APIs or want official-level SDK polish
- Stainless:
- Often the best choice where it’s already used to provide official or near-official SDKs.
- Pagination, retries, and idempotency semantics are baked in by design for those APIs.
Verdict: For consuming APIs that already use Stainless, you typically get production-grade pagination, retry, and idempotency support immediately, no forks or extra tooling.
Practical decision guide
When choosing between Fern, Stainless, and Speakeasy for pagination/retries/idempotency—without custom forks—ask:
-
What’s your starting point?
- No spec, want design-first: lean toward Fern.
- Existing OpenAPI, want to keep it: lean toward Speakeasy.
- Using a provider with Stainless SDKs: Stainless is often optimal.
-
How much control vs outsourcing do you want?
- More ownership of the API model and generators: Fern or Speakeasy.
- Less ownership, more “SDK-as-a-service”: Stainless.
-
How important is cross-language uniformity?
- Fern and Speakeasy both emphasize consistent semantics across SDK languages, driven from a single source (Fern schema or OpenAPI).
- Stainless emphasizes high quality per supported API; uniformity is strong within each API’s ecosystem.
-
Do you want to tweak behavior without touching codegen?
- Fern: adjust the API design/IR and generator config.
- Speakeasy: adjust OpenAPI + Speakeasy config/policies.
- Stainless: typically no direct config; you rely on what Stainless + the provider ship (often sufficient).
Conclusion
For the specific question—Fern vs Stainless vs Speakeasy: which supports pagination/retries/idempotency patterns without custom forks?—the key takeaway is:
- All three can support these patterns without forking generators.
- Fern and Speakeasy give you configuration- and spec-driven control to model pagination, retries, and idempotency for your own APIs.
- Stainless delivers these patterns as part of well-crafted SDKs, especially where it’s used behind official clients.
If your priority is max control with minimal maintenance, pick the tool that best matches your API design workflow:
- Design-first with a powerful IR → Fern
- OpenAPI-first with platform features → Speakeasy
- Provider-driven, official-level SDK polish → Stainless
In all three cases, pagination, retries, and idempotency can be first-class citizens in your SDKs without you maintaining custom forks—provided you model and configure them clearly in the tool’s preferred way.