Tutorial 01
Quickstart: your first request
Generate an API key, verify it, and read your first page of inventory, in about five minutes.
Everything in this tutorial runs against the live API at https://api.stockpilot.dev. You need a Stockpilot account on the Growth tier or higher, because API access is gated to those plans.
Get a credential pair
Stockpilot authenticates with a static client ID and secret, not OAuth. In the Stockpilot app, open Settings → API and generate a pair. You get two values:
X-CLIENT-ID: sp_live_a1b2c3d4e5f6
X-CLIENT-SECRET: sk_live_9f8e7d6c5b4a3210
The secret is shown once. Store it the way you would store a database password.
Both headers go on every request. There is no token exchange, no refresh, and no Authorization header. Sending a bearer token instead will get you a 401.
Export them so the rest of this page is copy-pasteable:
export SP_CLIENT_ID="sp_live_a1b2c3d4e5f6"
export SP_CLIENT_SECRET="sk_live_9f8e7d6c5b4a3210"
Verify the credentials
Before writing any integration code, confirm the pair works. /auth/who-is is the cheapest call in the API and tells you which organization you are acting as.
curl -s https://api.stockpilot.dev/auth/who-is \
-H "X-CLIENT-ID: $SP_CLIENT_ID" \
-H "X-CLIENT-SECRET: $SP_CLIENT_SECRET"
A working pair returns your organization:
{
"organization": "Acme Commerce BV",
"organization_id": 4812,
"plan": "growth"
}
If you get a 401, the pair is wrong or incomplete. If you get a 403, the credentials are valid but the plan does not include API access.
Read the rate-limit headers
Every response carries the current state of your budget. Ask for the headers on that same call:
curl -sI https://api.stockpilot.dev/auth/who-is \
-H "X-CLIENT-ID: $SP_CLIENT_ID" \
-H "X-CLIENT-SECRET: $SP_CLIENT_SECRET" | grep -i ratelimit
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 299
X-RateLimit-Reset: 47
X-RateLimit-Policy: read-single
Limits are advisory today, since Stockpilot does not return 429, but they tell you which bucket a call falls into, and a well-behaved integration paces itself against them. See Rate limits and idempotency for the four buckets and what lands in each.
Read your first page of inventory
Now something real. List endpoints paginate with page and page_size:
curl -s "https://api.stockpilot.dev/inventory?page_size=5" \
-H "X-CLIENT-ID: $SP_CLIENT_ID" \
-H "X-CLIENT-SECRET: $SP_CLIENT_SECRET"
{
"count": 1284,
"page": 1,
"page_size": 5,
"results": [
{
"id": 90211,
"sku": "TSHIRT-BLK-M",
"barcode": "8712345678906",
"name": "Heavyweight Tee Black / M",
"quantity": 42,
"offered_stock": 38,
"location": "A1-001-01",
"threshold": "5u"
}
]
}
Two fields deserve attention immediately:
quantityis the physical count.offered_stockis what Stockpilot actually advertises to your sales channels: the sum of warehouses that feed the offer, minus buffer stock. This is the number most integrations want. The inventory model explains why they differ.
page_size defaults to 100 and caps at 1000. Prefer one paginated list call over many single lookups; they come out of different rate-limit buckets.
Fetch a single item
When you already know an identifier, use the single-item endpoint. It accepts exactly one of id, sku or barcode. Sending two is an error.
curl -s "https://api.stockpilot.dev/inventory/get?sku=TSHIRT-BLK-M" \
-H "X-CLIENT-ID: $SP_CLIENT_ID" \
-H "X-CLIENT-SECRET: $SP_CLIENT_SECRET"
The same thing in Python
A minimal client you can build on:
import os
import httpx
client = httpx.Client(
base_url="https://api.stockpilot.dev",
headers={
"X-CLIENT-ID": os.environ["SP_CLIENT_ID"],
"X-CLIENT-SECRET": os.environ["SP_CLIENT_SECRET"],
},
timeout=30.0,
)
who = client.get("/auth/who-is").json()
print("authenticated as", who["organization"])
page = client.get("/inventory", params={"page_size": 5}).json()
for item in page["results"]:
print(f"{item['sku']:<20} {item['quantity']:>5} on hand {item['offered_stock']:>5} offered")
Errors come back as JSON with a detail or error key, so a real client should check the status before parsing:
resp = client.get("/inventory/get", params={"sku": "DOES-NOT-EXIST"})
if resp.is_error:
raise RuntimeError(resp.json().get("detail", resp.text))
Where to go next
You now have a working credential pair and a client that can read. The three surfaces worth bookmarking:
- API reference covers all 74 operations, rendered for humans.
- llms.txt is the same reference, compact, for the model in your editor.
- openapi.json is the raw schema, for generating a typed client.
The last two are regenerated from the schema on every request, so they cannot drift from the API.
