# Building an API business

URL: /start/tutorials/building-an-api-business · Id: `page:start/tutorials/building-an-api-business`

> Build a Startup that sells VAT rate lookups by the call, with a Dataset, a Meter, a free Offer, and a usage Price billed monthly.

You build `ratebook.dev`, a [Startup](/business/nouns/startup) that sells a [Dataset](/business/nouns/dataset) of VAT rates as an API, priced by the call. Developers and their agents subscribe, get a key, and pay for what they use.

## What you build
The Startup sells two [Offers](/business/nouns/offer): 1,000 free lookups a month, and $2 per 1,000 lookups beyond that. A [Meter](/business/nouns/meter) counts every keyed call, and billing reads the Meter, never a hand count.

You define the data and the prices. The [Autonomous data API](/start/templates/autonomous-data-api) [Startup Template](/business/nouns/startup-template) supplies the rest: the routes, the MCP server, the OpenAPI document, API keys, the Meter, and the [Site](/business/nouns/site).

Launch reads 11 checks for `ratebook.dev`, a [Autonomous data API](/start/templates/autonomous-data-api) Startup.

| Check | Id | Kind | Applies to | Passes when |
| --- | --- | --- | --- | --- |
| Offers published | `live-offers` | `registry` | Every Startup | At least one Offer is published with a price and terms; launch refuses a Startup without one. |
| Work defined | `live-work` | `registry` | Every Startup | Every published Offer has a workflow that delivers it, with each step assigned to software, an Agent, or a person. |
| People assigned | `live-people` | `registry` | Every Startup | Every role the workflows need is held by an Agent or a person, with instructions and limits. |
| Site published | `live-site` | `ui` | Every Startup | The site serves home, pricing, terms, and privacy pages under the Brand, readable by people and agents. |
| Test run passed | `live-test-run` | `transcript` | Every Startup | A sample Customer completes every published Offer end to end in test, with no real charge or message. |
| Portal ready | `live-portal` | `ui` | Every Startup | The customer portal on the site shows each Customer their orders, deliveries, invoices, and messages. |
| Agent API open | `live-agent-api` | `contract` | Every Startup | The API, MCP server, OpenAPI document, llms.txt, and agents.json serve every published Offer and pass conformance. |
| Payouts connected | `live-payouts` | `contract` | Every Startup | Stripe takes payments for the Startup and pays out to its bank account. |
| Address resolves | `live-address` | `contract` | Every Startup | The canonical Domain serves the Site over HTTPS with a valid certificate and, where it embeds a payment form, Stripe lists it as a payment method domain. |
| Five slots at Commitment | `live-slots` | `grade` | Every Startup | All five Founding Hypothesis slots hold Commitment Evidence from outside people. Slots `customer`, `problem`, `approach`, `switch`, `differentiation` at Commitment or stronger, outside Evidence only. |
| Sources secured | `live-sources` | `registry` | [Dataset API](/products/templates/dataset-api), [Directory](/products/templates/directory) | Every dataset source is licensed, crawled within its terms, or synthetic. |

You need `node` 20 or later and a terminal. Every command runs in the test environment, so no card is charged.

## Steps
1. To create the Startup, run `create startups` with the template:

   ```bash
   npm create startups ratebook.dev -- --template autonomous-data-api
   cd ratebook
   ```

   ```text
   Created Ratebook in test
     Startup      startup_1
     Template     Autonomous data API
     Dataset      dataset_1
     Claim token  ct_9Lp3wQ
   ```

2. To define what the Dataset holds, create a [Noun](/business/nouns/noun):

   ```bash
   npx startup-builder do Noun.create --json '{
     "name": "Rate",
     "properties": [
       { "name": "country", "type": "string", "required": true },
       { "name": "standard", "type": "number", "required": true },
       { "name": "reduced", "type": "number" },
       { "name": "validFrom", "type": "date", "required": true }
     ]
   }'
   ```

   In the test environment the definition applies at once: `https://api.sb/ratebook.dev/rates` now serves `Rate`.

3. To pass the launch check on data rights, record the [Source](/business/nouns/source) of the data:

   ```bash
   npx startup-builder do Source.create --json '{
     "acquired": "owned",
     "terms": "Compiled from rates each tax authority publishes for reuse."
   }'
   ```

   Every Source states how it was acquired. A Dataset sells only data it has the right to sell.

4. To load the data, create the first records:

   ```bash
   npx startup-builder do Rate.create --json '{"country":"DE","standard":19,"reduced":7,"validFrom":"2007-01-01"}'
   npx startup-builder do Rate.create --json '{"country":"FR","standard":20,"reduced":5.5,"validFrom":"2014-01-01"}'
   ```

5. To find the Meter that counts lookups, list the Meters:

   ```bash
   npx startup-builder show /meters
   ```

   ```json
   { "meters": [{ "$id": "https://api.sb/ratebook.dev/meters/meter_1", "unit": "calls" }] }
   ```

6. To price the API, create the two Offers. The Meter counts the lookups each one bills:

   ```bash
   FREE=$(npx startup-builder do Offer.create --json '{
     "price": { "amount": 0, "currency": "USD" },
     "interval": "month",
     "atBound": "upgrade",
     "terms": "1,000 lookups a month, free."
   }' | jq -r '.offer["$id"] | split("/") | last')
   PAID=$(npx startup-builder do Offer.create --json '{
     "price": { "amount": 0.002, "currency": "USD" },
     "interval": "month",
     "terms": "$2 per 1,000 lookups, billed monthly in arrears."
   }' | jq -r '.offer["$id"] | split("/") | last')
   ```

   Zero is a [Price](/business/nouns/price), so the free Offer is sold like any other. Each [Customer](/business/nouns/customer)'s free lookups count first.

7. [Publish](/business/nouns/offer/publish) both Offers:

   ```bash
   npx startup-builder do Offer.publish --json '{"offer":"'"$FREE"'"}'
   npx startup-builder do Offer.publish --json '{"offer":"'"$PAID"'"}'
   ```

8. To provision the routes, keys, and Site, [build](/business/nouns/startup/build) the Startup:

   ```bash
   npx startup-builder do Startup.build --json '{"startup":"startup_1"}'
   ```

9. To test as a developer, [subscribe](/business/nouns/offer/subscribe) a sample Customer to the paid Offer:

   ```bash
   npx startup-builder do Offer.subscribe --json '{"offer":"'"$PAID"'"}'
   ```

   The [Subscription](/business/nouns/subscription) grants an [Entitlement](/business/nouns/entitlement) to read `Rate`, and the response holds the Customer's own `sk_test_` key.

10. To make a lookup as that Customer, call the API with the Customer's key:

    ```bash
    curl https://api.sb/ratebook.dev/rates/DE \
      -H "Authorization: Bearer $CUSTOMER_KEY"
    ```

    ```json
    { "rate": { "$id": "https://api.sb/ratebook.dev/rates/DE", "country": "DE", "standard": 19, "reduced": 7 } }
    ```

    The Meter now reads one call for that Customer. The same lookup works through the SDK, the CLI, and MCP, and each one counts.

11. To connect payouts, [claim](/business/nouns/startup/claim) the Startup, then finish payouts onboarding at the link it prints:

    ```bash
    npx startup-builder do Startup.claim --json '{"startup":"startup_1","claimToken":"ct_9Lp3wQ"}'
    ```

12. [Launch](/business/nouns/startup/launch):

    ```bash
    npx startup-builder do Startup.launch --json '{"startup":"startup_1"}'
    ```

## Result
The Startup is Live. A developer or an agent finds the API through its Site, OpenAPI document, MCP server, or `llms.txt`, subscribes, and calls with its own key.

Each call ticks the Meter. At month end the Meter's count becomes an [Invoice](/business/nouns/invoice), collected from the card on file.

## Next steps
- [Datasets](/products/data/datasets): Sources, freshness, and the promise a Dataset makes.
- [Earning models](/products/selling/earning-models): usage, subscription, and the rest.
- [Building a service business](/start/tutorials/building-a-service-business): the same path for a warranted outcome.