---
title: "Start a Marketplace"
url: /quickstart/marketplace
---

# Start a Marketplace

> A live API that sells work many suppliers do under your brand, each sale charged when the buyer accepts it and split on Stripe.

You need Node 20 and a GitHub account. The examples use `research.apis.dev`, which stands for your domain; 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 marketplace
   ✓ wrote definition.ts: a Marketplace
     next: npx apis.dev dev
   ```

   `init` writes a [Marketplace](/patterns/marketplace.md) with one kind of supplier, one kind of order and your share.

2. **Write the file**

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

   /** Company research from verified researchers, ordered in one call and paid when you accept it. */
   export default define({
     name: 'research', domain: 'research.apis.dev',
     Researcher: {                                 // a supplier: verified, paid on Stripe
       supplier: { verify: ['identity', 'tax'], payout: 'stripe' },
       name: 'string', orders: '<-Order.researcher',
     },
     Order: {                                      // Noun: what the buyer orders and watches
       researcher: '->Researcher.orders', company: 'url', answer: 'string?',
       status: {
         place: [], fill: { in: 'placed', role: 'researcher' },
         accept: 'filled', contest: 'filled',
       },
     },
     offer: {                                      // Offer: charged once, when accepted
       'Order.place': { price: 40, when: 'accepted', accept: 'customer',
                        acceptWithin: '5d', remedy: 'refund' },
       take: 0.15,                                 // your share of each sale, split on Stripe
     },
   })
   ```

   A Researcher is a supplier: verified for identity and tax, and paid out on Stripe. `take` is your share of every sale.

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
   ```

   Place, fill and accept an Order on localhost. In your [Test twin](/reference/platform.md#test-twin) you play the researcher, and no sale splits.

4. **Publish it**

   ```sh title="Terminal"
   $ npx apis.dev deploy
   ✓ signed in with GitHub
   ✓ definition.ts: research on research.apis.dev
   ✓ Stripe connected: each sale splits between your account and the supplier's
   ✓ 1 Noun, 9 Verbs, 1 price
   ✓ Researcher: a supplier, verified for identity and tax, paid out on Stripe
       Order    place, get, list, update, delete, restore, fill, accept, contest
       Order.place     40 once, when accepted
       take            15% of each sale, split on Stripe
   ✓ published v1 to https://research.apis.dev: 11 Interfaces
       REST              /orders
       CLI               npx research
       MCP               /mcp
       SDK               npm i research, pip install research
       Docs              /docs
       OpenAPI           /openapi.json
       llms.txt          /llms.txt
       agents.json       /.well-known/agents.json
       Pricing           /pricing
       Landing site      /
       Developer portal  /dashboard
   ✓ Test key: research_test_ktzmZSU8e7mt2MYSTYd7nt4SXSRTc73b1e09
   ```

   Each researcher signs up in the developer portal, is verified, and connects a Stripe account before taking an Order.

5. **Read the price list**

   ```json title="/pricing"
   {
     "currency": "USD",
     "rates": [
       { "name": "Order.place",
         "price": 40, "unit": "outcome",
         "when": "accepted" }
     ]
   }
   ```

   Buyers and their agents read one price, and when it is charged: on acceptance, refunded in full when a contest is upheld.

6. **Order with no credits**

   **curl**

   ```sh title="Request"
   curl https://research.apis.dev/orders \
     -H "Authorization: Bearer $RESEARCH_KEY" \
     -H "Prefer: code=402" \
     --json '{"company":"acme.com"}'
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Research } from 'research'

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

   **Python**

   ```python title="Python"
   import os
   from research import Research

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

   **CLI**

   ```sh title="Terminal"
   $ npx research login --key $RESEARCH_KEY
   $ npx research orders place --company acme.com
   error 402 payment_required
   price 0 USD · req_56f0e5df9bfe58ff57bb
   ```

   **MCP**

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

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

   {
     "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_3f315ab24c48d81d12bd"
       }
     }
   }
   ```


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

   {
     "type": "https://research.apis.dev/docs/errors#payment_required",
     "title": "Payment Required",
     "status": 402,
     "detail": "The credit balance is 0; Order.place holds 40 until accepted.",
     "code": "payment_required",
     "instance": "req_56f0e5df9bfe58ff57bb",
     "offers": [
       {
         "kind": "credits",
         "price": 50,
         "checkout": "https://research.apis.dev/checkout/credits-50"
       },
       {
         "kind": "credits",
         "price": 200,
         "checkout": "https://research.apis.dev/checkout/credits-200"
       }
     ]
   }
   ```

   An Order holds its price from the buyer's credits until they accept. With none to hold, it answers 402 at price 0, with packs that cover it.

7. **Buy credits and order**

   **curl**

   ```sh title="Request"
   curl https://research.apis.dev/orders \
     -H "Authorization: Bearer $RESEARCH_KEY" \
     --json '{"company":"acme.com"}'
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Research } from 'research'

   const api = new Research({ key: process.env.RESEARCH_KEY })
   const { data, meta } = await api.placeOrder({ company: 'acme.com' })
   // data: {
   //   'id': 'order_1',
   //   'company': 'acme.com',
   //   'status': 'placed',
   //   'createdAt': '2026-10-26T09:00:00Z',
   //   'updatedAt': '2026-10-26T09:00:00Z'
   // }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from research import Research

   api = Research(key=os.environ["RESEARCH_KEY"])
   data, meta = api.place_order(company="acme.com")
   # data: {
   #   "id": "order_1",
   #   "company": "acme.com",
   #   "status": "placed",
   #   "createdAt": "2026-10-26T09:00:00Z",
   #   "updatedAt": "2026-10-26T09:00:00Z"
   # }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx research login --key $RESEARCH_KEY
   $ npx research orders place --company acme.com
   {
     "id": "order_1",
     "company": "acme.com",
     "status": "placed",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T09:00:00Z"
   }
   price 0 USD · req_0dd03e1e44bf1e4dd4e7
   ```

   **MCP**

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

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"order_1\",\"company\":\"acme.com\",\"status\":\"placed\",\"createdAt\":\"2026-10-26T09:00:00Z\",\"updatedAt\":\"2026-10-26T09:00:00Z\"}"
         }
       ],
       "structuredContent": {
         "id": "order_1",
         "company": "acme.com",
         "status": "placed",
         "createdAt": "2026-10-26T09:00:00Z",
         "updatedAt": "2026-10-26T09:00:00Z"
       },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_e798ec199b127fa4a417"
       }
     }
   }
   ```


   ```http title="Response"
   HTTP/1.1 201 Created
   Location: /orders/order_1
   X-Action-Id: act_1
   X-Cost: price=0, currency="USD"
   X-Request-Id: req_0dd03e1e44bf1e4dd4e7

   {
     "id": "order_1",
     "company": "acme.com",
     "status": "placed",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T09:00:00Z"
   }
   ```

   After Checkout, the Order answers 201 at price 0 and holds $40. A verified researcher takes it.

8. **A researcher fills it**

   **curl**

   ```sh title="Request"
   curl https://research.apis.dev/orders/order_1/fill \
     -H "Authorization: Bearer $RESEARCH_KEY" \
     --json '{"answer":"Dana Ito, head of sales"}'
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Research } from 'research'

   const api = new Research({ key: process.env.RESEARCH_KEY })
   const { data, meta } = await api.fillOrder('order_1', { answer: 'Dana Ito, head of sales' })
   // data: {
   //   'id': 'order_1',
   //   'researcher': 'researcher_7',
   //   'company': 'acme.com',
   //   'answer': 'Dana Ito, head of sales',
   //   'status': 'filled',
   //   'createdAt': '2026-10-26T09:00:00Z',
   //   'updatedAt': '2026-10-26T13:20:00Z'
   // }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from research import Research

   api = Research(key=os.environ["RESEARCH_KEY"])
   data, meta = api.fill_order("order_1", answer="Dana Ito, head of sales")
   # data: {
   #   "id": "order_1",
   #   "researcher": "researcher_7",
   #   "company": "acme.com",
   #   "answer": "Dana Ito, head of sales",
   #   "status": "filled",
   #   "createdAt": "2026-10-26T09:00:00Z",
   #   "updatedAt": "2026-10-26T13:20:00Z"
   # }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx research login --key $RESEARCH_KEY
   $ npx research orders fill order_1 --answer "Dana Ito, head of sales"
   {
     "id": "order_1",
     "researcher": "researcher_7",
     "company": "acme.com",
     "answer": "Dana Ito, head of sales",
     "status": "filled",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T13:20:00Z"
   }
   price 0 USD · req_c4a0aa61c4cc556666f6
   ```

   **MCP**

   ```sh title="Request"
   curl https://research.apis.dev/mcp \
     -H "Authorization: Bearer $RESEARCH_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "fillOrder",
       "arguments": { "id": "order_1", "answer": "Dana Ito, head of sales" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"order_1\",\"researcher\":\"researcher_7\",\"company\":\"acme.com\",\"answer\":\"Dana Ito, head of sales\",\"status\":\"filled\",\"createdAt\":\"2026-10-26T09:00:00Z\",\"updatedAt\":\"2026-10-26T13:20:00Z\"}"
         }
       ],
       "structuredContent": {
         "id": "order_1",
         "researcher": "researcher_7",
         "company": "acme.com",
         "answer": "Dana Ito, head of sales",
         "status": "filled",
         "createdAt": "2026-10-26T09:00:00Z",
         "updatedAt": "2026-10-26T13:20:00Z"
       },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_1eb72b4f80312cf8bfc4"
       }
     }
   }
   ```


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

   {
     "id": "order_1",
     "researcher": "researcher_7",
     "company": "acme.com",
     "answer": "Dana Ito, head of sales",
     "status": "filled",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T13:20:00Z"
   }
   ```

   The researcher fills the Order with their own [Key](/reference/platform.md#key); only the `researcher` Role can. The buyer sees the answer and who wrote it.

9. **Accept it, and the sale splits**

   **curl**

   ```sh title="Request"
   curl -X POST https://research.apis.dev/orders/order_1/accept \
     -H "Authorization: Bearer $RESEARCH_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Research } from 'research'

   const api = new Research({ key: process.env.RESEARCH_KEY })
   const { data, meta } = await api.acceptOrder('order_1')
   // data: {
   //   'id': 'order_1',
   //   'researcher': 'researcher_7',
   //   'company': 'acme.com',
   //   'answer': 'Dana Ito, head of sales',
   //   'status': 'accepted',
   //   'createdAt': '2026-10-26T09:00:00Z',
   //   'updatedAt': '2026-10-26T15:05:00Z'
   // }
   // meta.cost: { price: 40, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from research import Research

   api = Research(key=os.environ["RESEARCH_KEY"])
   data, meta = api.accept_order("order_1")
   # data: {
   #   "id": "order_1",
   #   "researcher": "researcher_7",
   #   "company": "acme.com",
   #   "answer": "Dana Ito, head of sales",
   #   "status": "accepted",
   #   "createdAt": "2026-10-26T09:00:00Z",
   #   "updatedAt": "2026-10-26T15:05:00Z"
   # }
   # meta.cost: { "price": 40, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx research login --key $RESEARCH_KEY
   $ npx research orders accept order_1
   {
     "id": "order_1",
     "researcher": "researcher_7",
     "company": "acme.com",
     "answer": "Dana Ito, head of sales",
     "status": "accepted",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T15:05:00Z"
   }
   price 40 USD · req_465afb1378d7d52735fc
   ```

   **MCP**

   ```sh title="Request"
   curl https://research.apis.dev/mcp \
     -H "Authorization: Bearer $RESEARCH_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "acceptOrder", "arguments": { "id": "order_1" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"order_1\",\"researcher\":\"researcher_7\",\"company\":\"acme.com\",\"answer\":\"Dana Ito, head of sales\",\"status\":\"accepted\",\"createdAt\":\"2026-10-26T09:00:00Z\",\"updatedAt\":\"2026-10-26T15:05:00Z\"}"
         }
       ],
       "structuredContent": {
         "id": "order_1",
         "researcher": "researcher_7",
         "company": "acme.com",
         "answer": "Dana Ito, head of sales",
         "status": "accepted",
         "createdAt": "2026-10-26T09:00:00Z",
         "updatedAt": "2026-10-26T15:05:00Z"
       },
       "_meta": {
         "cost": { "price": 40, "currency": "USD" },
         "requestId": "req_bc2ca8aea7fd6e7dcbf1"
       }
     }
   }
   ```


   ```http title="Response"
   HTTP/1.1 200 OK
   X-Action-Id: act_4
   X-Cost: price=40, currency="USD"
   X-Request-Id: req_465afb1378d7d52735fc

   {
     "id": "order_1",
     "researcher": "researcher_7",
     "company": "acme.com",
     "answer": "Dana Ito, head of sales",
     "status": "accepted",
     "createdAt": "2026-10-26T09:00:00Z",
     "updatedAt": "2026-10-26T15:05:00Z"
   }
   ```

   Acceptance charges the held $40 once. A buyer who contests instead brings it to you, and a contest upheld refunds it in full.

10. **Get paid**

   *One sale, split once · USD*

   | Line | Share | Amount |
   | --- | --- | --- |
   | The buyer pays | `Order.place`, accepted | 40.00 |
   | The supplier's Stripe account | 85% | 34.00 |
   | Your Stripe account | 15% | 6.00 |

   ```text title="Stripe receipt · October 2026 · USD"
   Order.place     1 × 40        40.00
   ───────────────────────────────────
   Total                         40.00
   Paid from 50.00 of credits    40.00
   Balance                       10.00
   ```

   Stripe splits the one charge: the researcher is paid out on their own account, and your take lands in yours. The buyer's receipt shows one line.

## Result
Your API answers on https://research.apis.dev: Orders that verified researchers fill, charged $40 once, when the buyer accepts.

Stripe splits each charge: $34 to the researcher and $6 to you. You wrote no supplier onboarding, no held funds and no payout code.

## Next steps
- **[Start a Dataset API](/quickstart/dataset-api.md) →** sell the facts about a thing, priced per answer.
- [Marketplace](/patterns/marketplace.md): suppliers and their catalog, an order from placed to paid, and the split.
- [Commerce](/commerce.md): charges, holds, splits and payouts on Stripe.