---
title: "Start a Dataset API"
url: /quickstart/dataset-api
---

# Start a Dataset API

> A live, priced API people and agents pay for in about 30 seconds, written as one file: a Source, a Mapping and a price.

You need Node 20 and a GitHub account. The first deploy answers at once on `companyfacts.apis.dev`; your own domain is one line.

1. **Call the sample**

   ```sh title="Request"
   curl https://apis.dev/samples/companyfacts/companies/acme.com
   ```

   This is the API you are about to build, answering keyless from this site: the record, its price in `X-Cost`, and a request id. Press Try it, or paste the line into a terminal.

2. **Start the folder**

   ```sh title="Terminal"
   $ npx apis.dev init dataset-api
   ✓ wrote definition.ts: a Dataset API
     next: npx apis.dev dev
   ```

   `init` writes the file you build here.

3. **Write the file**

   ```ts title="definition.ts"
   import { define, secret } from 'apis.dev'

   /** Any company by its domain, with its headcount and sector. */
   export default define({
     name: 'companyfacts', domain: 'companyfacts.apis.dev',
     sources: {                                    // Source: the API you resell
       firms: { origin: 'https://firms.example.com', key: secret('FIRMS_KEY') },
     },
     Company: {                                    // Noun: the shape you promise
       domain:    { type: 'url!#',   from: 'firms', map: 'website' },  // Mapping
       employees: { type: 'number?', from: 'firms', map: 'headcount' },
       sector:    { type: 'string?', from: 'firms', map: 'industry' },
     },
     offer: { 'Company.get': 0.002, 'Company.search': 0.005 },  // Offer: the price
   })
   ```

   Your Source answers in its own field names, and each `map` picks one of them; `!#` makes `domain` the key. The comment above the file is its pitch on every [Interface](/every-product.md). The Source's credential never sits in the file: set it once with `npx apis.dev secrets set FIRMS_KEY`.

4. **Try it locally**

   ```sh title="Terminal"
   $ npx apis.dev dev
   ✓ firms answered in 212 ms; their field → your field:
       website    → domain     "acme.com"
       headcount  → employees  240
       industry   → sector     "Software"
   ✓ every Interface on http://localhost:8787, against your Test twin
     reloads on every save
   ```

   `dev` asks the Source once and prints the record it maps, then serves every Interface on localhost against your [Test twin](/reference.md#test-twin).

5. **Publish it**

   ```sh title="Terminal"
   $ npx apis.dev deploy
   ✓ signed in with GitHub
   ✓ definition.ts: companyfacts on companyfacts.apis.dev
   ✓ Stripe connected: each Customer pays into your own account
   ✓ 1 Noun, 2 Verbs, 1 Source, 2 prices
       Company  get, search
       Company.get     0.002 a call
       Company.search  0.005 a call
   ✓ published v1 to https://companyfacts.apis.dev: 11 Interfaces
       REST              /companies/{domain}, /companies?sector=
       CLI               npx companyfacts
       MCP               /mcp
       SDK               npm i companyfacts, pip install companyfacts
       Docs              /docs
       OpenAPI           /openapi.json
       llms.txt          /llms.txt
       agents.json       /.well-known/agents.json
       Pricing           /pricing
       Landing site      /
       Developer portal  /dashboard
   ✓ Test key: companyfacts_test_NNTTj83hyuMNRNk85nzuQNW8h8y3wu33c155
   ```

   The first run signs you in with GitHub and connects Stripe to your own account. Your product answers at once on `https://companyfacts.apis.dev`, with each Verb, each price and each Interface printed, and a Test key. Set `domain` to a domain of your own and deploy again to move every Interface there; deploy checks it once by a TXT record.

6. **Call it with the Test key**

   **curl**

   ```sh title="Request"
   curl https://companyfacts.apis.dev/companies/acme.com \
     -H "Authorization: Bearer $COMPANYFACTS_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Companyfacts } from 'companyfacts'

   const api = new Companyfacts({ key: process.env.COMPANYFACTS_KEY })
   const { data, meta } = await api.getCompany('acme.com')
   // data: { 'domain': 'acme.com', 'employees': 240, 'sector': 'Software' }
   // meta.cost: { price: 0.002, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from companyfacts import Companyfacts

   api = Companyfacts(key=os.environ["COMPANYFACTS_KEY"])
   data, meta = api.get_company("acme.com")
   # data: { "domain": "acme.com", "employees": 240, "sector": "Software" }
   # meta.cost: { "price": 0.002, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx companyfacts login --key $COMPANYFACTS_KEY
   $ npx companyfacts companies get acme.com
   { "domain": "acme.com", "employees": 240, "sector": "Software" }
   price 0.002 USD · req_3cd59a9a1ede10c5bf21
   ```

   **MCP**

   ```sh title="Request"
   curl https://companyfacts.apis.dev/mcp \
     -H "Authorization: Bearer $COMPANYFACTS_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "getCompany", "arguments": { "domain": "acme.com" } }
   }'
   ```

   ```http title="Response"
   HTTP/1.1 200 OK
   X-Cost: price=0.002, currency="USD"
   X-Request-Id: req_f9cf5fc65654d907192f

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [ … ],
       "structuredContent": {
         "domain": "acme.com",
         "employees": 240,
         "sector": "Software"
       },
       "_meta": {
         "cost": { "price": 0.002, "currency": "USD" },
         "requestId": "req_f9cf5fc65654d907192f"
       }
     }
   }
   ```


   ```http title="Response"
   HTTP/1.1 200 OK
   X-Cost: price=0.002, currency="USD"
   X-Request-Id: req_3cd59a9a1ede10c5bf21

   { "domain": "acme.com", "employees": 240, "sector": "Software" }
   ```

   Call with the [Test key](/reference.md#test-key) the first run printed: its calls never bill. A Live [Key](/reference.md#key) answers the same, and bills.

7. **Get paid**

   ```text title="Stripe receipt · October 2026 · USD"
   Company.get    12,400 × 0.002  24.80
   Company.search  1,300 × 0.005   6.50
   ────────────────────────────────────
   Total                          31.30
   Paid from 50.00 of credits     31.30
   Balance                        18.70
   ```

   Every Live call your Customers make lands on their Stripe receipt, a line per Verb, and the money lands in your own Stripe account. This is one Customer's month.

8. **See every Interface**

   *Every Interface, on https://companyfacts.apis.dev*

   | Interface | Path |
   | --- | --- |
   | REST | `/companies/{domain}`, `/companies?sector=` |
   | CLI | `npx companyfacts` |
   | MCP | `/mcp` |
   | SDK | `npm i companyfacts`, `pip install companyfacts` |
   | Docs | `/docs` |
   | OpenAPI | `/openapi.json` |
   | llms.txt | `/llms.txt` |
   | agents.json | `/.well-known/agents.json` |
   | Pricing | `/pricing` |
   | Landing site | `/` |
   | Developer portal | `/dashboard` |

9. **Change the price**

   ```ts title="definition.ts"
     offer: { 'Company.get': 0.002, 'Company.search': 0.005 },  // [!code --]
     offer: { 'Company.get': 0.003, 'Company.search': 0.005 },  // [!code ++]
   ```


   ```sh title="Terminal"
   $ npx apis.dev deploy
   ✓ definition.ts: 1 Noun, 2 Verbs, 1 Source, 2 prices
   ✓ published v2 to https://companyfacts.apis.dev: every Interface at once
   ```

   *The price on three Interfaces*

   | Interface | At 0.002 | At 0.003 |
   | --- | --- | --- |
   | `price` in `X-Cost` | `price=0.002` | `price=0.003` |
   | `/pricing` | `"price": 0.002` | `"price": 0.003` |
   | `/llms.txt` | `($0.002)` | `($0.003)` |

   Publish again, and all three move at once: the price in your docs is the price on the reply. A higher price is a breaking change: each Key keeps the [Version](/reference.md#version) it first called until it sends `Api-Version`.

10. **Ask for one nobody has**

   **curl**

   ```sh title="Request"
   curl https://companyfacts.apis.dev/companies/nobody.example.com \
     -H "Authorization: Bearer $COMPANYFACTS_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Companyfacts } from 'companyfacts'

   const api = new Companyfacts({ key: process.env.COMPANYFACTS_KEY })
   const { error, meta } = await api.getCompany('nobody.example.com')
   // error: { status: 404, code: 'not_found' }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from companyfacts import Companyfacts

   api = Companyfacts(key=os.environ["COMPANYFACTS_KEY"])
   error, meta = api.get_company("nobody.example.com")
   # error: { "status": 404, "code": "not_found" }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx companyfacts login --key $COMPANYFACTS_KEY
   $ npx companyfacts companies get nobody.example.com
   error 404 not_found
   price 0 USD · req_84326ad21bc488454616
   ```

   **MCP**

   ```sh title="Request"
   curl https://companyfacts.apis.dev/mcp \
     -H "Authorization: Bearer $COMPANYFACTS_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "getCompany",
       "arguments": { "domain": "nobody.example.com" } }
   }'
   ```

   ```http title="Response"
   HTTP/1.1 200 OK
   X-Cost: price=0, currency="USD"
   X-Request-Id: req_f9cf5fc65654d907192f

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "isError": true,
       "content": [ … ],
       "structuredContent": { "status": 404, "code": "not_found" },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_f9cf5fc65654d907192f"
       }
     }
   }
   ```


   ```http title="Response"
   HTTP/1.1 404 Not Found
   Content-Type: application/problem+json
   X-Cost: price=0, currency="USD"
   X-Request-Id: req_84326ad21bc488454616

   {
     "type": "https://companyfacts.apis.dev/docs/errors#not_found",
     "title": "Not Found",
     "status": 404,
     "detail": "No Company has domain nobody.example.com.",
     "code": "not_found",
     "instance": "req_84326ad21bc488454616"
   }
   ```

   A miss answers a [Problem](/reference.md#errors) with its code, and its price is 0.

## Result
Your API answers on https://companyfacts.apis.dev: 2 Verbs on 11 Interfaces, each call drawing its posted price from credits your Customers buy through Stripe Checkout, and a miss is free. You wrote no Verb, no docs and no price page.

## Next steps
- **[Start a Headless SaaS](/quickstart/headless-saas.md) →** Your Customers write each field. You pick the shape, its lifecycle and the plans.
- [Dataset API](/patterns/dataset-api.md): how a field is filled, kept or live.
- [What every product gets](/every-product.md): everything else the file gives you.