# How to Ship a Public API Your First Customers Will Actually Use
> How to ship a public API small SaaS customers will use: scope v1 from real jobs, safe API keys, clear errors, rate limits, versioning, docs, a 14-day plan.

```yaml
url: "https://makerhunt.io/studio/how-to-ship-a-public-api-your-first-customers-will-use"
markdown: "https://makerhunt.io/studio/how-to-ship-a-public-api-your-first-customers-will-use.md"
title: How to Ship a Public API Your First Customers Will Actually Use
type: studio_article
category: Growth
tags: "public API, public API for SaaS, how to build a public API, API keys best practices, API rate limiting, API versioning, API documentation, OpenAPI, makers, Growth"
featured_image: "https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx90wy5QfuUMGtDgAeTkzHpnxayuvbZw12cLKE8"
published_at: "2026-10-26T09:46:56.680Z"
updated_at: "2026-10-26T09:46:57.291Z"
```

## About this studio article

How to ship a public API small SaaS customers will use: scope v1 from real jobs, safe API keys, clear errors, rate limits, versioning, docs, a 14-day plan.

## Article

To ship a public API your first customers will actually use, start from three to five jobs real customers already asked for, expose only the resources those jobs need, and make the boring parts dependable: prefixed API keys with read or write scopes, one consistent error format, clear rate limits with a 429 and a Retry-After header, idempotency keys on anything that creates data, and a quickstart that gets someone from new key to first successful call in five minutes. A small API that never surprises anyone beats a big one that mirrors your whole database. Then watch three numbers: keys created, first successful call, and weekly active keys.

Below, how to build a public API step by step: when you need one, how to scope v1, keys, errors, limits, retries, versioning, docs, adoption, a made-up example and a 14-day plan.

## Do you actually need a public API yet?

Most small products hear "do you have an API?" long before they need one. The question is cheap. Supporting an API forever is not. So look at what is behind the request.

Signals that a public API for SaaS is worth it now:

- **The same job keeps coming up.** Several customers want to push data in or pull it out on a schedule.
- **People already script around you.** Someone scrapes your pages or exports a CSV by hand every morning.
- **A deal depends on it.** A B2B buyer needs to connect you to internal tools, and "no API" ends the conversation.
- **Glue tools come up.** Users ask for Zapier, Make or n8n, which would need an API from you anyway.

Signals that something smaller will do:

- One customer wants one report. A scheduled CSV export solves it this week.
- Everyone asking wants the same tool. One native connection may serve them better; see [choosing your first integration when users ask for everything](https://makerhunt.io/studio/how-to-choose-your-first-integration-when-users-ask-for-everything).
- Nobody can say what they'd do with it. "It would be nice to have an API" is not a job.

My rule: if I can name three customers and the exact job each wants to automate, I build the API. If not, I ship the export and keep a list.

## What should the first version of your API do?

Scope v1 from real jobs, not database tables. Write each job as one sentence in the customer's words, then list the calls it needs.

1. **Collect 3 to 5 jobs.** "When an order ships in our system, create a record in yours." "Every night, pull yesterday's entries into our warehouse." "Let our support tool look up a customer's status."
2. **Map each job to calls.** Usually a list, a get-by-ID and maybe one create. Write them as lines like `GET /v1/batches` and `POST /v1/batches`.
3. **Cut everything else.** No delete unless a job needs it. No admin settings, no billing.
4. **Check with the customers who asked.** Send the lines and ask, "Could you do your job with only these?" That's your spec review.

Six endpoints covering four real jobs is a good v1. Every endpoint is a promise: adding one later is easy, taking one away means emailing everyone who built on it.

## How should you shape resources and responses?

You don't need a style guide on day one, but you need a few rules you never break. Customers forgive a small API, not one where every endpoint behaves differently.

- **Use nouns for resources.** `/v1/batches`, `/v1/batches/{id}`. The HTTP method carries the verb.
- **Same JSON shape everywhere.** Pick snake_case or camelCase and keep it. Lists always return a `data` array plus a "more items" flag.
- **Stable IDs.** String IDs that never change. A type prefix like `bat_` makes logs and support emails easier to read.
- **Timestamps in one format, in UTC.** [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) defines a timestamp format for internet protocols that is a profile of ISO 8601, which means values like `2026-10-26T09:30:00Z`.
- **Cursor pagination from the start.** Return a page of items plus a cursor for the next page, instead of page numbers. Stripe's list endpoints, for example, take a `limit` and a `starting_after` object ID, and return `has_more`. Cursors don't skip or repeat rows when new data arrives, which is exactly what nightly sync jobs hit.
- **Don't leak internals.** No column names you might rename, no fields you're unsure about. Adding a field later is easy; removing one breaks people.

![image](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx9hG1tsdRJkXTmPie5h04ySvxCrwdZbuRtDGBl)

## How should API keys work?

API keys best practices come down to one idea: assume a key will leak one day, and make that day boring. Here's what I'd ship in v1.

- **Prefixed keys.** Put a readable prefix on every key, such as `hl_live_` and `hl_test_`. GitHub explained why it moved to this pattern in [its post on new authentication token formats](https://github.blog/engineering/platform-security/behind-githubs-new-authentication-token-formats/): prefixes like `ghp_` make tokens identifiable, which helps secret scanning, and the underscore separator isn't a Base64 character, so random strings don't look like tokens by accident.
- **Show the key once.** Display it at creation, let the user copy it, then never show it again. Store only a hash plus the last four characters, so the dashboard can say "key ending in 7f2a".
- **Scopes, even if there are only two.** Read and write is enough for v1. Stripe's [authentication docs](https://docs.stripe.com/api/authentication) describe restricted keys with a subset of permissions, which "limits the damage a bad actor could cause if they obtained the key". A reporting script should never hold a key that can delete things.
- **Many named keys.** One key per script or integration, so revoking a leaked one breaks nothing else.
- **Rotate and revoke in the UI.** Revoke is immediate. Rotation means create, swap, then revoke, so two keys must work at once.
- **Header only, never in URLs.** Accept keys in the `Authorization` header. URLs end up in logs and browser history.
- **Last used, on every key.** It helps customers clean up, and it's the cheapest usage data you'll collect.

## What should your API errors look like?

Use HTTP status codes honestly, and wrap every error in the same body. The codes tell a program what class of thing went wrong. The body tells the developer what to fix.

Status

Use it when

What the body should say

400

The request is malformed or a field is invalid

Which field, and what a valid value looks like

401

No key, or the key is wrong or revoked

"Missing or invalid API key", nothing more

403

The key is valid but lacks the scope

Which scope the call needs

404

The object doesn't exist for this account

The ID that wasn't found

409

A conflict, such as a reused idempotency key with different data

What conflicted

429

Too many requests

The limit, plus when to retry

500

Your bug

A request ID they can send you

For the body, you don't have to invent a format. [RFC 9457, Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html), defines a standard JSON error object served as `application/problem+json`, with members called `type`, `status`, `title`, `detail` and `instance`, and room for your own extension members. It replaced the older RFC 7807.

Add a stable error code customers can switch on, and a request ID on every response. Half of API support is "can you look up this request?"

## How do you rate limit without annoying good customers?

API rate limiting protects you from one runaway loop. It shouldn't punish a customer syncing normal amounts of data.

- **Limit per key, not only per IP.** Jobs often run from shared cloud IPs; per-key limits point at the script that misbehaves.
- **Start generous.** Set the first limit a few times above your heaviest real customer's need, then tighten with data. Rule of thumb, not a standard.
- **Answer with 429 and Retry-After.** MDN's page on [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429) describes it as the status for a client that "has sent too many requests in a given amount of time", and notes a Retry-After header may be included. The [Retry-After header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After) takes either an HTTP date or a number of seconds, so `Retry-After: 30` tells a well-written client exactly how long to wait.
- **Show the budget and document it.** Return remaining-requests headers so clients slow down before the wall, and put the limits in the docs. Surprise limits feel like outages.

## How do you make retries safe?

Networks fail halfway. A script sends "create this record", the connection drops, and it doesn't know if it worked. If the retry creates a second record, you've made a mess they must clean up.

The fix is an idempotency key. The client sends a unique value with every create; if it arrives again, you return the first result. [Stripe's idempotent requests docs](https://docs.stripe.com/api/idempotent_requests) are a good model to copy: the client generates the key (they suggest V4 UUIDs), sends it in an `Idempotency-Key` header, keys are up to 255 characters, and for their v1 API all POST requests accept one. Retries with the same key return the saved response, Stripe compares incoming parameters to the original and errors if they differ, and keys can be pruned after at least 24 hours.

For a small API: accept `Idempotency-Key` on every POST, store key, request fingerprint and response for a day, replay the stored response on a repeat, and return 409 if the key returns with different data.

## How should you handle API versioning?

Keep API versioning boring. A version in the path, like `/v1/`, is easy to see and route. Dated versions pinned per account are more flexible and more work; Stripe documents that model on [its API versioning page](https://docs.stripe.com/api/versioning). For a first API, `/v1/` is enough.

The bigger habit is additive change: new fields, endpoints and optional parameters. Tell clients to ignore fields they don't know. Renaming, removing or changing the meaning of a field waits for v2.

If you ever do retire something, two HTTP headers exist for saying so in machine-readable form: the Deprecation header in [RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html), and the Sunset header in [RFC 8594](https://www.rfc-editor.org/rfc/rfc8594.html), which carries the date a resource is expected to become unresponsive. The email to the people using that endpoint matters more.

## Where do webhooks fit in a first API?

Webhooks mean you call customers when something happens instead of them polling. Add them once a job really is "tell me when X happens": a few events, signed payloads, retries on failure. For how receivers get it wrong, see this piece on [why a Stripe webhook that works in test mode breaks in production](https://dev.to/posting-dude/your-stripe-webhook-works-in-test-mode-heres-how-it-breaks-in-production-198). Polling with cursors is a fine start.

![image](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx99TMVl2SYksFrxbCcIQZtwhXo1HyLjU3f6O7m)

## What API documentation does a first API need?

Good API documentation is the difference between "we have an API" and "people use it". V1 needs four pieces, none of them a big portal.

1. **An OpenAPI file.** The [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) defines a standard, programming language-agnostic description for HTTP APIs that both humans and computers can read. One file gives you reference docs, mocks and generated clients. Keep it next to the code.
2. **A five-minute quickstart.** Create a key, make one call with curl, see real data come back. Something like `curl -H "Authorization: Bearer hl_test_..." [your API host]/v1/batches`, then the exact response. If it takes you longer than five minutes on a clean account, fix the product.
3. **Copy-paste examples per job.** One short page per v1 job, with real requests and responses.
4. **A changelog.** Every new field and fix, dated. API users read changelogs closely. Here's how to [write a changelog that brings silent users back](https://makerhunt.io/studio/how-to-write-a-changelog-that-brings-silent-users-back).

Docs can also bring search traffic; that's a separate project, covered in this guide to [getting organic signups from product docs without a blog](https://indiehunt.io/studio/how-to-get-organic-signups-from-product-docs-without-a-blog).

## How do you know anyone is using your API?

Hearing nothing after launch is normal. Track three numbers to see where people stop:

1. **Keys created.** Low means nobody knows the API exists or key creation is hidden.
2. **First successful call.** Keys with at least one 2xx. A gap here means the quickstart is broken.
3. **Weekly active keys.** Any 2xx in the last 7 days. A drop means the API doesn't cover the job yet.

Also log top error codes per key. A key that makes fifty 400s and goes quiet is a customer who gave up. Email them the exact fix.

## A made-up example: Hopsledger opens its API

Hopsledger is a made-up product for this post: a batch log for small craft breweries. Every number below is invented to show the method, not real data.

After a year of support requests, the founder wrote down the jobs and built only what they needed.

Customer job (made up)

Calls in v1

Decision

Push finished batches into our accounting sheet every night

GET list of batches, filtered by updated time

Build

Create a batch from our recipe tool when brewing starts

POST batch, with idempotency key

Build

Show tank status on a screen in the taproom

GET batch by ID

Build

Let our distributor see stock levels

GET list of batches with a read-only key

Build (read scope covers it)

Change team members and permissions from a script

Admin endpoints

Not in v1, one request only

Bulk delete old batches

DELETE batches

Not in v1, CSV cleanup instead

That's three endpoints, two scopes, test and live keys with prefixes, problem-details errors, a per-key limit with 429 and Retry-After, and one quickstart. In this made-up story, the funnel after the first month showed plenty of keys created but few first calls; the quickstart assumed a field customers didn't have yet. One fix to the docs, no new endpoints.

## What traps should you avoid?

- **Mirroring your database.** An endpoint per table locks your internal schema into a public contract. Design around jobs.
- **One master key per account.** No scopes, no names, no way to revoke one integration without breaking the rest.
- **Inconsistent errors.** A string here, an object there, a 200 with "error" inside. Every client must special-case you.
- **Silent breaking changes.** Renaming a field "because nobody uses it" is how you find out who does.
- **Docs that drift.** Hand-written reference pages fall behind the code within weeks. Keep the spec next to the code.

## A 14-day plan to ship your first public API

Rule of thumb for a solo maker or a pair, alongside normal work. Stretch it, don't skip steps.

Days

Do this

Done when

1 to 2

Collect 3 to 5 customer jobs, map them to calls

A list of 4 to 8 endpoint lines

3

Send the list to the customers who asked

At least two say "yes, that covers it"

4 to 5

Write the OpenAPI file, decide naming, IDs, timestamps, pagination

Spec reviewed against your own rules

6 to 7

Keys: prefixes, hashing, scopes, names, revoke, last used

Create, use and revoke a key end to end

8 to 9

Build the endpoints with one error format and request IDs

Every endpoint returns the same shapes

10

Rate limits with 429 and Retry-After; idempotency on POST

A retry test creates exactly one record

11

Quickstart, job examples, limits page, changelog entry

A friend gets a first call in 5 minutes

12

Usage logging: keys created, first call, weekly active keys, top errors

One query shows the funnel

13

Private release to the customers who asked

Each one made a successful call

14

Fix what they hit, then announce it in the changelog

API linked from the app and the docs

## FAQ

### REST or GraphQL for a small team?

For a first public API, I'd pick plain REST with JSON. It works with curl and every no-code tool, and caching and rate limits stay simple. GraphQL makes sense when clients need many shapes of the same data and you can handle query cost limits. Most small products aren't there yet.

### Should the API be free?

Including API access on paid plans is common, and for v1 that's usually enough. Don't design pricing around the API before you know how people use it. Watch the usage funnel for a few months, then decide.

### How do I stop people abusing the API?

Require a key for every call, give new accounts lower limits, make most keys read-only, rate limit and log per key, and revoke fast when something looks wrong.

### Do I need an SDK?

Not at first. Curl examples and an OpenAPI file get most developers going. Write an SDK when one language keeps showing up in your logs and you're ready to maintain it.

## Build the small API your customers asked for

A first public API doesn't need to be big. It needs to do a few real jobs reliably, fail in ways people understand, and come with a working quickstart. Let the next endpoint come from the next real request. When it's live, you can put your product in front of more makers on [MakerHunt](https://makerhunt.io/).

## Links

- Article (HTML): https://makerhunt.io/studio/how-to-ship-a-public-api-your-first-customers-will-use
- AI-friendly Markdown: https://makerhunt.io/studio/how-to-ship-a-public-api-your-first-customers-will-use.md

## Explore

- [Studio index](https://makerhunt.io/studio)
- [Blog](https://makerhunt.io/blog)
- [Browse projects](https://makerhunt.io/projects)

_HTML version: https://makerhunt.io/studio/how-to-ship-a-public-api-your-first-customers-will-use_