---
title: "Start a Function API"
url: /quickstart/function-api
---

# Start a Function API

> A live API that sells answers, each priced per call by who works it out: a model in seconds, an agent in minutes.

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

1. **Start the folder**

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

   `init` writes a [Function API](/patterns/function-api.md) that publishes as it is. This track builds a shorter one, with two Verbs.

2. **Write the file**

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

   /** Answers about any company by its domain: what it sells in seconds, its head of sales in minutes. */
   export default define({
     name: 'summaries', domain: 'summaries.apis.dev',
     Company: {                                    // Noun: the Noun it acts on
       domain:    'url!#',
       summarize: generative('Two sentences on what {domain} sells',
         { returns: { summary: 'string' } }),      // Verb: a model writes it
       research:  agentic('Find the head of sales at {domain}',
         { returns: { headOfSales: 'string' } }),  // Verb: an agent finds it
     },
     offer: {                                      // Offer: a price per call, by kind
       'Company.summarize': 0.02, 'Company.research': 0.5,
     },
   })
   ```

   Each Verb names its [Function](/patterns/function-api.md) kind, and the kind sets how long it takes. `returns` types the answer on every [Interface](/every-product.md).

3. **Try it locally**

   ```sh title="Terminal"
   $ npx apis.dev dev
   ✓ every Interface on http://localhost:8787, against your Test twin
     reloads on every save
   ```

   Call each Verb on localhost before anyone pays for one. A model or an agent run in your [Test twin](/reference/platform.md#test-twin) spends your allowance, never a Customer's credits.

4. **Publish it**

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

   Deploy prints each Verb with its price. The first run signs you in with GitHub and connects Stripe to your own account.

5. **Read the price list**

   ```json title="/pricing"
   {
     "currency": "USD",
     "rates": [
       { "name": "Company.summarize",
         "price": 0.02, "unit": "call" },
       { "name": "Company.research",
         "price": 0.5, "unit": "call" }
     ]
   }
   ```

   Your Customers and their agents read this before the first call, on `/pricing` and on the offer card.

6. **Call it with no credits**

   **curl**

   ```sh title="Request"
   curl -X POST https://summaries.apis.dev/companies/acme.com/summarize \
     -H "Authorization: Bearer $SUMMARIES_KEY" \
     -H "Prefer: code=402"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Summaries } from 'summaries'

   const api = new Summaries({ key: process.env.SUMMARIES_KEY })
   const { error, meta } = await api.summarizeCompany('acme.com')
   // error: { status: 402, code: 'payment_required' }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from summaries import Summaries

   api = Summaries(key=os.environ["SUMMARIES_KEY"])
   error, meta = api.summarize_company("acme.com")
   # error: { "status": 402, "code": "payment_required" }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx summaries login --key $SUMMARIES_KEY
   $ npx summaries companies summarize acme.com
   error 402 payment_required
   price 0 USD · req_e01eab0d5cdfbb1619d4
   ```

   **MCP**

   ```sh title="Request"
   curl https://summaries.apis.dev/mcp \
     -H "Authorization: Bearer $SUMMARIES_KEY" \
     -H "Prefer: code=402" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "summarizeCompany", "arguments": { "id": "acme.com" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "isError": true,
       "content": [
         {
           "type": "text",
           "text": "{\"status\":402,\"code\":\"payment_required\"}"
         }
       ],
       "structuredContent": { "status": 402, "code": "payment_required" },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_ee3ed8704e1b3cd592b5"
       }
     }
   }
   ```


   ```http title="Response"
   HTTP/1.1 402 Payment Required
   Content-Type: application/problem+json
   X-Cost: price=0, currency="USD"
   X-Request-Id: req_e01eab0d5cdfbb1619d4

   {
     "type": "https://summaries.apis.dev/docs/errors#payment_required",
     "title": "Payment Required",
     "status": 402,
     "detail": "The credit balance is 0; Company.summarize draws 0.02 from it.",
     "code": "payment_required",
     "instance": "req_e01eab0d5cdfbb1619d4",
     "offers": [
       {
         "kind": "credits",
         "price": 10,
         "checkout": "https://summaries.apis.dev/checkout/credits-10"
       },
       {
         "kind": "credits",
         "price": 50,
         "checkout": "https://summaries.apis.dev/checkout/credits-50"
       },
       {
         "kind": "credits",
         "price": 200,
         "checkout": "https://summaries.apis.dev/checkout/credits-200"
       }
     ]
   }
   ```

   A new Customer's first call answers 402 at price 0. Its `offers` are credit packs, each a Stripe Checkout link.

7. **Buy credits and call again**

   **curl**

   ```sh title="Request"
   curl -X POST https://summaries.apis.dev/companies/acme.com/summarize \
     -H "Authorization: Bearer $SUMMARIES_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Summaries } from 'summaries'

   const api = new Summaries({ key: process.env.SUMMARIES_KEY })
   const { data, meta } = await api.summarizeCompany('acme.com')
   // data: {
   //   'id': 'act_1',
   //   'verb': 'Company.summarize',
   //   'status': 'done',
   //   'output': {
   //     'summary': 'Acme sells pipeline software to B2B sales teams.'
   //   }
   // }
   // meta.cost: { price: 0.02, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from summaries import Summaries

   api = Summaries(key=os.environ["SUMMARIES_KEY"])
   data, meta = api.summarize_company("acme.com")
   # data: {
   #   "id": "act_1",
   #   "verb": "Company.summarize",
   #   "status": "done",
   #   "output": { "summary": "Acme sells pipeline software to B2B sales teams." }
   # }
   # meta.cost: { "price": 0.02, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx summaries login --key $SUMMARIES_KEY
   $ npx summaries companies summarize acme.com
   {
     "id": "act_1",
     "verb": "Company.summarize",
     "status": "done",
     "output": { "summary": "Acme sells pipeline software to B2B sales teams." }
   }
   price 0.02 USD · req_fa5075382229d67f1ce9
   ```

   **MCP**

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

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"act_1\",\"verb\":\"Company.summarize\",\"status\":\"done\",\"output\":{\"summary\":\"Acme sells pipeline software to B2B sales teams.\"}}"
         }
       ],
       "structuredContent": {
         "id": "act_1",
         "verb": "Company.summarize",
         "status": "done",
         "output": {
           "summary": "Acme sells pipeline software to B2B sales teams."
         }
       },
       "_meta": {
         "cost": { "price": 0.02, "currency": "USD" },
         "requestId": "req_f08587376f0b234c2cd7"
       }
     }
   }
   ```


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

   {
     "id": "act_1",
     "verb": "Company.summarize",
     "status": "done",
     "output": { "summary": "Acme sells pipeline software to B2B sales teams." }
   }
   ```

   After Checkout, the same call answers with the summary, and `X-Cost` draws $0.02 from the credits.

8. **Ask for research**

   **research**

   ```sh title="Request"
   curl -X POST https://summaries.apis.dev/companies/acme.com/research \
     -H "Authorization: Bearer $SUMMARIES_KEY"
   ```

   ```http title="Response"
   HTTP/1.1 202 Accepted
   Location: /actions/act_2
   Retry-After: 60
   X-Cost: price=0.5, currency="USD"
   X-Request-Id: req_0a6b1dbf299819cd7076

   {
     "id": "act_2",
     "verb": "Company.research",
     "status": "pending",
     "reason": "Running",
     "poll": "/actions/act_2"
   }
   ```

   **get**

   ```sh title="Request"
   curl https://summaries.apis.dev/actions/act_2 \
     -H "Authorization: Bearer $SUMMARIES_KEY"
   ```

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

   {
     "id": "act_2",
     "verb": "Company.research",
     "status": "done",
     "output": { "headOfSales": "Dana Ito" },
     "provenance": "Inferred"
   }
   ```

   An agent takes minutes, so the call answers 202 at once with an [Action](/workflow.md#the-lifecycle) to poll. Its answer arrives stamped Inferred, and a failed Action costs nothing.

9. **Get paid**

   ```text title="Stripe receipt · October 2026 · USD"
   Company.summarize 1,800 × 0.02    36.00
   Company.research     40 × 0.5     20.00
   ───────────────────────────────────────
   Total                             56.00
   Paid from 200.00 of credits       56.00
   Balance                          144.00
   ```

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

## Result
Your API answers on https://summaries.apis.dev: 2 Verbs, a model's answer at once for $0.02 and an agent's as an Action for $0.50.

Each is typed on every Interface. A call with no credits answers 402 with packs on Stripe Checkout, and a failed Action costs nothing. You wrote no prompt pipeline, no job queue and no metering.

## Next steps
- **[Start an Agent API](/quickstart/agent-api.md) →** sell a worker instead of an answer, billed by what each run consumes.
- [Function API](/patterns/function-api.md): the four kinds, one Verb behind several, and pricing by kind.
- [Runtime](/runtime.md): the gate every call passes, and the Action every Verb answers.