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.
Open in
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.
Start the folder
Terminal $ npx apis.dev init marketplace ✓ wrote definition.ts:a Marketplace next: npx apis.dev dev initwrites a Marketplace with one kind of supplier, one kind of order and your share.Write the file
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.
takeis your share of every sale.Try it locally
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 you play the researcher, and no sale splits.
Publish it
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.
Read the price list
/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.
Order with no credits
POST research.apis.dev/ordersRequest curl https://research.apis.dev/ orders \ -H "Authorization: Bearer $RESEARCH_KEY" \ -H "Prefer: code= 402" \ --json '{"company": "acme.com"}' 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" } ] } 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' } 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" } ] } 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" } 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" } ] } Terminal $ npx research login --key $RESEARCH_KEY $ npx research orders place --company acme.com error 402 payment_required price 0 USD · req_56f0e5df9bfe58ff57bbResponse 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" } ] } 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" } } }' 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" } } } 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.
Buy credits and order
POST research.apis.dev/ordersRequest curl https://research.apis.dev/ orders \ -H "Authorization: Bearer $RESEARCH_KEY" \ --json '{"company": "acme.com"}' 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" } 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' } 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" } 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" } 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" } 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 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" } 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" } } }' 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" } } } After Checkout, the Order answers 201 at price 0 and holds $40. A verified researcher takes it.
A researcher fills it
POST research.apis.dev/orders/order_1/fillRequest curl https://research.apis.dev/ orders/ order_1/ fill \ -H "Authorization: Bearer $RESEARCH_KEY" \ --json '{"answer": "Dana Ito, head of sales"}' 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" } 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' } 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" } 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" } 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" } 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 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" } 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" } } }' 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" } } } The researcher fills the Order with their own Key; only the
researcherRole can. The buyer sees the answer and who wrote it.Accept it, and the sale splits
POST research.apis.dev/orders/order_1/acceptRequest curl -X POST https://research.apis.dev/ orders/ order_1/ accept \ -H "Authorization: Bearer $RESEARCH_KEY" 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" } 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' } 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" } 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" } 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" } 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 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" } 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" } } }' 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" } } } Acceptance charges the held $40 once. A buyer who contests instead brings it to you, and a contest upheld refunds it in full.
Get paid
| 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 |
Order.place 1 × 40 40.00
───────────────────────────────────
Total 40.00
Paid from 50.00 of credits 40.00
Balance 10.00Stripe 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 → sell the facts about a thing, priced per answer.
- Marketplace: suppliers and their catalog, an order from placed to paid, and the split.
- Commerce: charges, holds, splits and payouts on Stripe.