Library / SDK
arlyon/async-stripe avatar
arlyon/async-stripe

async-stripe, generated Rust bindings for the Stripe API

Async (and blocking!) Rust bindings for the Stripe API

755 stars188 forksRustApache-2.0

At a glance

What is it?
arlyon/async-stripe generates its entire API surface from Stripe's own OpenAPI specification every week through CI, so the types track the upstream API without a human writing them. The library splits into per-area crates with feature flags so a project compiles only the parts of Stripe it uses, and it offers both asynchronous and blocking clients.
Who is it for?
Adopt async-stripe if you are writing Rust against the Stripe API and want types that follow the upstream specification automatically, and you can live with release candidates while the 1.0 line settles. Skip it if you need a stable, released version today, since the current published releases are 1.0.0-rc.9 candidates.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the code generation buys you

async-stripe is described as a convenient, performant and strongly typed Rust library for the Stripe HTTP API. The important detail is that it provides asynchronous bindings for the entire Stripe API surface and that it is generated directly from Stripe's official OpenAPI specification, updated weekly via CI.

That weekly CI job fetches the latest spec, regenerates the code and opens a pull request. The consequence for a user is that a new Stripe feature is available in the library almost immediately, and the types match what the API actually returns rather than what a maintainer believed it returned three years ago.

The cost is that you are taking a dependency on generated code you did not write, and on a generation pipeline you did not run. When a type looks wrong, the first question is whether the spec was wrong rather than whether the library was.

The performance story comes from how the two directions of JSON handling are split. The README explains that using serde for both serialization and deserialization in previous versions resulted in excessive codegen size and long compile times, because the code generation creates a very large number of types. The current approach is a hybrid: serde is used for serializing the request parameters you send to Stripe, where its flexibility and rich feature set help with building complex requests, and miniserde is used for deserializing responses, since a minimal, high performance JSON library reduces compile times and final binary size against a full serde dependency. If you need serde's Deserialize trait on the Stripe response types inside your own application, you can enable the deserialize feature on any of the stripe crates.

The licence is MIT OR Apache-2.0, and the crate requires Rust 1.89.0 with edition 2024.

The crate split and how you pick what to compile

The modular design is what makes the library usable on a large surface. The README notes that the API is split into multiple crates so you only compile what you need, and the table gives the mapping.

The client crate, async-stripe, is always required. The resource crates then cover the API areas: stripe-core for customers, charges, payment intents and refunds, stripe-payment for payment methods, payment links and sources, stripe-billing for invoices, subscriptions, plans and quotes, stripe-connect for accounts, account links and transfers, stripe-fraud for Radar and reviews, stripe-checkout for checkout sessions, stripe-webhook for receiving and deserialising webhook events, stripe-product for products, prices, coupons and tax rates, stripe-issuing for card creation and management, stripe-terminal for in-person payments, stripe-treasury for financial accounts and money movement, and stripe-misc for tax, identity, reporting and Sigma.

The point of the split is compile time and binary size. A project that only creates customers should not pay for the code generation of the Treasury API.

The HTTP client is chosen with a feature flag on the core crate, and the README gives the table. The default-tls flag, which is the default, gives you tokio with hyper and native-tls. The rustls-tls-webpki-roots flag gives tokio with hyper and rustls using webpki-roots, and rustls-tls-native gives tokio with hyper and rustls using the native certificate store. The async-std-surf flag swaps the runtime and client to async-std with surf and native-tls. There is also a blocking flag, which gives a synchronous interface on top of tokio.

That last row explains something about the crate's name. It is called async-stripe and it is async-first, but the same library also provides the blocking variant, which matters because a lot of Stripe traffic comes from code that is not itself async, and forcing an executor on it is often more disruptive than a blocking call.

The README also points to a crate_info.md file in the repository for a complete mapping of every API object to its crate and feature flag, and notes that it is being actively improved. Failing that, the generated/ directory in the repository lists all available API crates and their modules.

Creating a customer and the builder pattern

The quick start shows two steps. First the dependencies, with the main crate plus a resource crate for the API area you want and the runtime:

toml
[dependencies]
async-stripe = "=1.0.0-rc.5"
async-stripe-core = { version = "=1.0.0-rc.5", features = ["customer"] }
tokio = { version = "1", features = ["full"] }

Two details there are worth copying. The exact version is pinned with an equals sign, which for a release candidate is not caution but necessity, since the pin guarantees you get the same API on the next build. And the feature flag on the resource crate selects the customer feature, which is what limits the generated code you pull in.

Then the call itself:

rust
use stripe::Client;
use stripe_core::customer::CreateCustomer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Initialize the client
    let secret_key = std::env::var("STRIPE_SECRET_KEY").expect("Missing STRIPE_SECRET_KEY in env");
    let client = Client::new(secret_key);

    // 2. Create a customer using the builder pattern
    let customer = CreateCustomer::new()
        .name("Alexander Lyon")
        .email("[email protected]")
        .description("A fake customer that is used to illustrate the examples in async-stripe.")
        .metadata([(String::from("async-stripe"), String::from("true"))])
        .send(&client)
        .await?;

    println!("Successfully created customer: {} ({})", customer.name.unwrap_or_default(), customer.id);

    Ok(())
}

The shape is a builder that ends in a send call, and the send takes the client and returns a future. That ordering is the ergonomic point the README makes: the builder flows from request construction to sending, so the call site reads as one expression rather than a request object plus a separate invocation.

The secret key comes from the environment and the client is constructed from it, which keeps the key out of the source. The error handling uses the Result return with a boxed error, and the send is awaited with the question mark operator, so a Stripe error propagates as a Rust error.

The examples directory in the repository is organised by what you are trying to do rather than by endpoint: endpoints, errors, pagination, and webhook examples for actix, axum and rocket. The webhook ones matter because webhook handling is the part of a Stripe integration that is hardest to get right, and having examples for three popular Rust web frameworks removes the guesswork.

The release candidate problem and the migration

Before you commit to this library, there are three things to read, all of which the repository points you at.

The first is the migration guide, MIGRATION.md, because the API style changed with the builder pattern. That is a large surface and a large change, and a migration guide is the difference between an afternoon and a week. The builder pattern that the quick start shows is the current style, and it replaced an earlier one in which you constructed a request struct and passed it to a client method.

The second is the release documentation. The project uses release-plz for versioning, with a release-plz.toml at the root, and the version comes from the workspace Cargo.toml at 1.0.0-rc.9. The release candidate version is repeated in the quick start's dependency example, which pins exactly 1.0.0-rc.5, and that pin is the correct thing to copy. An exact version on a release candidate is not over-caution: it guarantees the same generated API surface on the next build, and with generated code a version bump is an API change even when the release notes look trivial.

The third is the examples directory, which is organised by task rather than by endpoint. There are endpoints/, errors/, pagination/ and webhook examples for actix, axum and rocket. The errors and pagination directories are the ones worth studying before you write any code, because both are places where a Stripe integration quietly goes wrong. Error handling in particular is worth copying rather than inventing, because the difference between a card decline and a programming error determines whether your code retries.

And the webhook examples, one per framework, exist because webhook handling is the hardest part of a Stripe integration. Each event has to be verified against the endpoint's signing secret before you act on it, and the stripe-webhook crate exists to do that verification. The separate crate is deliberate: it means a binary that only makes outgoing API calls does not carry the webhook verification code at all.

If your organisation only permits stable dependencies, this library is currently not a candidate, since the published versions are release candidates with no 1.0 yet. In that case, a hand-written client over a small number of endpoints, or a different language, is the pragmatic answer.

Webhooks, the blocking client and what to check

The webhook crate is separate, and the reason is worth understanding. Webhooks arrive as HTTP requests to an endpoint you host, and each one carries an event payload that has to be verified against the endpoint's signing secret before you trust it. The stripe-webhook crate handles that securely, and keeping it separate means you do not pull it into a binary that only makes outgoing API calls.

The repository has a dedicated async-stripe-webhook member in the workspace and a generated/ directory with the resource crates, plus async-stripe-client-core, async-stripe-parser and async-stripe-types as hand-written crates around the generated code. The parser crate is what turns raw HTTP responses into the typed structures, and the types crate holds the shared types.

If you are choosing between this and the other Rust Stripe libraries, the comparison comes down to three things. Generation frequency is the first: a weekly pull from the OpenAPI spec is about as current as a binding can be. The second is the crate split, which is unusual and directly reduces your compile time if you only use one API area. The third is the release cadence, and here the honest answer is that the published versions are 1.0.0-rc.9 candidates, with the async-stripe, async-stripe-types and async-stripe-webhook crates all at rc.9 as of September 2026. There is no stable 1.0 yet.

So the checks before you commit are these. Read the migration guide, since the README points to a MIGRATION.md and the API style changed with the builder pattern. Read the release documentation, since the project uses release-plz for versioning and the release process may have expectations about how you pin. And decide deliberately about the release candidate: if your organisation requires stable releases only, this library is not currently a dependency you can take, and a hand-written client or an official Stripe SDK on another language may be the answer instead.

If you do proceed, pin the exact version, use Stripe's test mode keys until the flow is verified, and use the webhook examples for the framework you already run.

Editorial conclusion

Adopt async-stripe if you are writing Rust against the Stripe API and want types that follow the upstream specification automatically, and you can live with release candidates while the 1.0 line settles. Skip it if you need a stable, released version today, since the current published releases are 1.0.0-rc.9 candidates. Verify first by pinning the exact version with an equals sign in your Cargo.toml, choosing a TLS feature flag, and running a test against Stripe's test mode before you wire it into a payment path.

Frequently asked questions

How does async-stripe stay current with the Stripe API?

The code is generated directly from Stripe's official OpenAPI specification, and a CI job runs weekly to fetch the latest spec, regenerate the code and open a pull request. The README says this means new Stripe features and updates are available in the library almost immediately.

Why does async-stripe use both serde and miniserde?

Because the two directions have different needs. serde is used for serializing request parameters, where its flexibility helps build complex requests, and miniserde is used for deserializing responses, since a minimal JSON library reduces compile times and binary size against a full serde dependency. A deserialize feature on any stripe crate enables serde's Deserialize on the response types.

Which crates do I need to add to Cargo.toml?

The async-stripe client crate is always required, plus one resource crate for the API areas you use, for example stripe-core with the customer feature for customers. The full split covers payments, billing, connect, fraud, checkout, webhook, product, issuing, terminal, treasury and miscellaneous.

Can async-stripe be used without an async runtime?

Yes. There is a blocking feature flag, listed in the client choice table, which gives a synchronous interface using tokio. The other options are default-tls with tokio and hyper, rustls-tls-webpki-roots, rustls-tls-native and async-std-surf for async-std with surf.

Is async-stripe released as a stable version?

Not yet. The published releases are 1.0.0-rc.9 candidates for async-stripe, async-stripe-types and async-stripe-webhook, dated September 2026, and the quick start shows an exact version pin with the equals sign for that reason. The crate requires Rust 1.89.0.

Official sources

  1. arlyon/async-stripe on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/arlyon-async-stripe.svg)](https://hysenlabs.com/projects/arlyon-async-stripe)

Was this useful?