Bottle API Reference
Market and government data, weather and environment, plus the building blocks for your next app: notes, lists, queues, streaks, leaderboards, and more. One REST API, one key.
What are you building?
One key, one JSON contract. Pick the part you need and the rest of this page gets out of the way.
Quickstart
Make your first Bottle API call in under 2 minutes.
Create an account & generate a key
Sign up at app.bottlebluellc.com, then visit your dashboard to generate an API key. Your key is an alphanumeric string that identifies your account on every request.
Signing up, viewing plans, generating a key and managing your subscription all work on the free plan. When you're ready to call the data endpoints, pick the plan that fits (Hobby, Pro, or Enterprise) and your key unlocks them instantly.
Store the key as an environment variable
export BBL_API_KEY="your_key_here"import os
# Set in your shell first: export BBL_API_KEY="your_key_here"
API_KEY = os.environ["BBL_API_KEY"]// Set in your shell first: export BBL_API_KEY="your_key_here"
const API_KEY = process.env.BBL_API_KEY;Never hard-code your key in source files. Use environment variables or a secrets manager.
Make your first request
The /math endpoint is a great first call: synchronous,
no extra parameters required, and it works on both hosts.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "tip", "amount": 50, "percent": 20}}'import os, requests
API_KEY = os.environ["BBL_API_KEY"]
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={"type": "percentage", "params": {"kind": "tip", "amount": 50, "percent": 20}},
)
print(resp.status_code, resp.json())const API_KEY = process.env.BBL_API_KEY;
const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ type: "percentage", params: { kind: "tip", amount: 50, percent: 20 } }),
});
const data = await resp.json();
console.log(data);Read the response
A successful call returns 200 OK with a JSON body.
Error responses include a requestId, so quote it when
contacting support.
{
"result": 10.0,
"meta": {
"category": "percent",
"formula_id": "percent:tip"
}
}That’s it. Every other endpoint follows the same pattern: set the header, send JSON, read JSON. Explore the sidebar to find the right endpoint for your use case.
Authentication
Send your API key in the x-api-key header on every request.
Requests without a valid key receive 401.
A request is authorized only when the key is active and the
owning account has an active paid subscription
(Hobby, Pro, or Enterprise). Free
gives you an account and an API key to sign up and get set up; upgrade to a
paid plan whenever you are ready to call the full API. A key without a paid
subscription receives
403 on the gated endpoints. The examples below read the key
from a BBL_API_KEY environment variable; never hard-code it.
export BBL_API_KEY="your_key_here"
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "tip", "amount": 50, "percent": 20}}'import os, requests
API_KEY = os.environ["BBL_API_KEY"]
resp = requests.get("https://api.aquabotany.com/v1/weather",
headers={"x-api-key": API_KEY},
params={"lat": 44.98, "lon": -93.26})
print(resp.json())const API_KEY = process.env.BBL_API_KEY;
const resp = await fetch("https://api.aquabotany.com/v1/weather?lat=44.98&lon=-93.26", {
headers: { "x-api-key": API_KEY }
});
console.log(await resp.json());Errors & rate limits
The API uses conventional HTTP status codes and returns a JSON error envelope
with a machine-readable code and a requestId.
| Status | Meaning |
|---|---|
| 400 | Malformed request (bad JSON or parameters). |
| 401 | Missing or invalid API key. |
| 403 | Authenticated but not allowed (usually no active subscription). |
| 404 | No such resource. |
| 429 | Rate limit exceeded. Limits are per-plan: Free 100 requests/day (10/min burst), Hobby 1,667/day (60/min), Pro 10,000/day (300/min), Enterprise 100,000/day (1,000/min), its published allowance. Above Enterprise, a Custom plan sets volume and rate limits per contract, agreed in writing. Codes: BURST_LIMIT_EXCEEDED / DAILY_LIMIT_EXCEEDED. Every 429 carries a Retry-After header giving the whole seconds until the window that tripped actually resets, so prefer it over a fixed backoff: a retry sooner than that is counted against your quota and cannot succeed. Browser clients can read it because the response also sets Access-Control-Expose-Headers: Retry-After. |
| 500 | Something went wrong on our side. Quote the requestId. |
| 502 | An upstream data provider returned an error or an unusable response. Code: UPSTREAM_ERROR. |
| 503 | An upstream data provider is unreachable. Code: UPSTREAM_UNAVAILABLE. |
| 504 | An upstream data provider did not respond in time. Code: UPSTREAM_TIMEOUT. |
Attribution
One line, one link, wherever you show Bottle API data.
Display the words Powered by Bottle API, linked to bottleapi.com. That is the requirement, in full.
What is required
- The text is "Powered by Bottle API", linked to
https://bottleapi.com. - It appears on every page or view that displays Bottle API data, not once on an About page.
- It is legible and visible on arrival. Normal reading size, normal contrast, in view without a hover, a tap or an expanded panel.
- It applies on every plan: Free, Hobby and Pro. An Enterprise agreement may vary or waive it in writing.
Paste this
<a href="https://bottleapi.com" rel="noopener">Powered by Bottle API</a>The link may carry rel="noopener" and may open in a new tab. Style it to
sit in your own design, in your own typeface and colours. Keep the five words and the
destination exactly as written.
Credit the original source too
Attribution to Bottle API is separate from, and additional to, attribution to the body that published a figure. Crediting us does not discharge a duty to say that a filing came from SEC EDGAR or that a reference rate came from the European Central Bank. Where the source is part of what you are showing, name it alongside us.
Using the data
Commercial use inside your own product is included on every paid plan. Call the API from your backend, show what comes back inside your application, and charge your own users for that application, including by subscription. They pay you for the product you built and they need no account with us.
Four things sit outside that. Handing our data onward to your users through an API, a feed, an export, a download or any other machine-readable interface needs an Enterprise agreement. So does keeping a standing copy of it, meaning bulk caching, mirroring or archiving. So does sustained use above the published allowance of your plan. And operating a service that offers data access in competition with ours is outside the licence altogether.
Short-lived caching of individual responses inside your own application is permitted and expected. Section 9 of the Terms of Use is the authority for all of this.
Enterprise is a published plan and you buy it yourself, alongside Hobby and Pro, from app.bottlebluellc.com. Keeping a standing copy of the data, and handing it onward to your users through a machine-readable interface, are included in the Enterprise plan and governed by the Terms of Use, so there is nothing separate to sign. Volume above the Enterprise allowance is arranged individually as a custom plan, by email to support@bottlebluellc.com.
Data platforms (the platform field)
The stateful endpoints (notes, lists, tasks, articles and their social
actions, streaks, environments, plants, devices, and the other building blocks)
are multi-tenant by platform. One set of endpoints can back
several apps and each account's own projects at the same time, without their
data colliding. Add an optional platform value to a request and it
segments the data that request reads or writes.
Note: using platform: "bb_aquabotany" puts you in
that product's data namespace, and the sign-in response echoes it back as
data_platform so you can send it here. What kind of app you signed
in from is a separate field, client, which never affects data.
How to send it
Send platform as a query-string parameter (any method) or as a
field in the JSON body (on writes). If both are present the query string wins.
Omit it to use your default namespace.
Values
| Value | Meaning |
|---|---|
| omitted or empty | Your personal default namespace. All data is already scoped to your account, so this is your private global space. |
my_project(any non-numeric string, up to 64 chars of letters, digits, and _.:-) | One of your own projects, created implicitly the first time you use the name. It is private to your account: with your key you only ever reach your own data, so two different accounts can safely reuse the same project name and never see each other's rows. |
bb_aquabotany(a product slug) | A Bottle Blue product namespace, named by a friendly slug: bb_aquabotany, bb_pocketquant, bb_bottleblue, bb_apparatus, bb_trackless. This is the preferred way to address a product. A slug is reserved, so it cannot also be used as a personal project name, and it must be a registered, active product or the request is rejected 404 UNKNOWN_PLATFORM. Any authenticated request may currently address a product namespace, including on the Free tier - a paid-plan requirement (Hobby, Pro, or Enterprise) is wired but not yet enforced at the platform-resolution layer. Standard per-endpoint gating and rate limits still apply. Signed-in users of a Bottle Blue app reach their own data in that app's namespace through their in-app subscription instead; an in-app subscription does not include API access. |
* | Wildcard: read across your own platforms (your projects and the default namespace). A Bottle Blue product namespace is a shared space rather than one of yours, so its rows are not included; read a product namespace by naming it. Reads only; writing to * is rejected 400 WILDCARD_WRITE. |
You cannot name a project a reserved word (platform,
platforms, all, none); those return
400 RESERVED_PLATFORM.
Sending platform: "default" is not an error; it is simply the same
as omitting the field, so default can never be a separate project.
A single item addressed by id lives in exactly one platform, so fetching it
under the wrong platform returns 404.
Your own platforms (your projects, the default namespace,
and the * wildcard) are private to your account: every read
returns only your own data. The one exception is a Bottle Blue
product platform (bb_), where a few features are
deliberately shared community spaces: the events, challenges and rewards
catalogs read as a shared feed there, and leaderboards and scoreboards are
shared boards. On your own project platforms even those are private to you.
Examples
# Write into a project namespace (platform in the body)
curl https://api.bottlebluellc.com/v1/notes \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seedlings", "content": "start basil", "platform": "my_garden"}'
# List just that project (platform as a query parameter)
curl "https://api.bottlebluellc.com/v1/notes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"
# List across every platform you own
curl "https://api.bottlebluellc.com/v1/notes?platform=*" \
-H "x-api-key: $BBL_API_KEY"import os, requests
API_KEY = os.environ["BBL_API_KEY"]
BASE = "https://api.bottlebluellc.com/v1"
# Write into a project namespace
requests.post(f"{BASE}/notes", headers={"x-api-key": API_KEY},
json={"title": "Seedlings", "content": "start basil", "platform": "my_garden"})
# List one platform, then across all of them
requests.get(f"{BASE}/notes", headers={"x-api-key": API_KEY}, params={"platform": "my_garden"})
requests.get(f"{BASE}/notes", headers={"x-api-key": API_KEY}, params={"platform": "*"})const API_KEY = process.env.BBL_API_KEY;
const BASE = "https://api.bottlebluellc.com/v1";
// Write into a project namespace
await fetch(`${BASE}/notes`, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ title: "Seedlings", content: "start basil", platform: "my_garden" })
});
// List one platform, then across all of them
await fetch(`${BASE}/notes?platform=my_garden`, { headers: { "x-api-key": API_KEY } });
await fetch(`${BASE}/notes?platform=*`, { headers: { "x-api-key": API_KEY } });Where it applies
Every stateful building block accepts platform: notes, lists,
queues, tasks, assets, articles and their social actions, streaks,
leaderboards, scoreboards, rewards, events, challenges, actions,
notifications, work orders, reminders, settings, image uploads,
environments, plants, devices, and commands. Utility
and read-only data endpoints (math, weather, market data, calculators,
government data) and the /auth and billing endpoints are global
and ignore it.
A few families behave slightly differently:
- Social (article likes, comments, shares, feed): a social action carries the same platform as its article, so likes and comments are scoped to the platform the article was published in.
- Streaks: a streak is inherently single-platform (one
profile, one date range), so
*is rejected here with 400WILDCARD_UNSUPPORTED. Use a specific platform or omit it. - Per-account routes: your reward-points balance and
redemption history are global to your account, so
GET /rewards/pointsandGET /rewards/historyignoreplatform. The same goes forGET /images(the presigned upload URL); the platform is recorded when the upload is registered withPOST /images.
The Link System (the /links endpoint)
Every other building block stores content. /links stores relationships: a small record connecting any two records in your account. Picture a gardening event, "Spring planting day". On its own it is just a calendar entry, but the day has moving parts: a task to water the new seedlings, and a reminder to buy mulch the day before. A link lets the event own each of those, so afterward you can ask one question that was impossible before - what belongs to Spring planting day? - and get the watering task and the reminder back. This section explains the idea; the full request and response reference lives in the /links endpoint operations further down the page.
A reference is an address
Both ends of a link are written as a ref: one string that names any record in the system, the way a postal address names one house. Here is the ref for the Spring planting day event:
bb:event:default:evt_8f3a2c| Piece | Meaning |
|---|---|
bb | Fixed prefix marking a Bottle Blue reference. |
event | Which building block the record lives in - here, an event. |
default | Which platform namespace, the same value you send as platform. See Data platforms. |
evt_8f3a2c | The id POST /events already handed back as eventId. Every endpoint returns its own id field (eventId, task_id, reminder.id, note_id, and so on) - any of them slots straight in here. |
Because one format addresses every entity, a single endpoint covers every combination of building blocks, including ones added later.
Linkable entities
note task list listitem setting asset article image reward action event challenge notification leaderboard scoreboard workorder reminder queueitem streak environment plant device command
Choosing a relationship type
Every link carries a type - the verb that says how the two records relate (has_task, has_reminder, and so on). The type is free-form: any string of letters, digits, and _ . - up to 64 characters is accepted. It is not a fixed enum - the API never checks the verb against a list, so the vocabulary is yours to choose to fit your domain. Omit it and the type defaults to related.
These are conventions we suggest, not values the API enforces - pick whatever reads well in your own data:
| Suggested verb | Reads as |
|---|---|
has_task | an event (or project) owns a task |
has_reminder | an event owns a reminder |
blocks | this record must be done before another can start |
depends_on | the reverse of blocks |
part_of | this record belongs to a larger one |
attachment | a file or image attached to a record |
related | a loose association (the default when type is omitted) |
A worked example
Build the Spring planting day event, give it a watering task and a mulch reminder, then ask what belongs to it.
# 1. Create the three records exactly as you do today
curl https://api.aquabotany.com/v1/events \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Spring planting day", "type": "gardening", "description": "Get the beds in.", "datetime": "2026-09-01T09:00:00"}'
# -> {"eventId": "evt_8f3a2c"}
curl https://api.aquabotany.com/v1/tasks \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action_type": "water", "details": {"plant": "seedlings", "amount_ml": 500}}'
# -> {"task_id": "tsk_abc123"}
curl https://api.aquabotany.com/v1/tasks/reminders \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Buy mulch", "due_at": "2026-08-30T08:00:00Z"}'
# -> {"reminder": {"id": "rem_45de67", ...}}
# 2. Connect them. The ids above become refs.
curl https://api.bottlebluellc.com/v1/links \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from": "bb:event:default:evt_8f3a2c", "to": "bb:task:default:tsk_abc123", "type": "has_task"}'
curl https://api.bottlebluellc.com/v1/links \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from": "bb:event:default:evt_8f3a2c", "to": "bb:reminder:default:rem_45de67", "type": "has_reminder"}'
# 3. What belongs to Spring planting day? (the task and the reminder come back)
curl "https://api.bottlebluellc.com/v1/links?from=bb:event:default:evt_8f3a2c" \
-H "x-api-key: $BBL_API_KEY"
# 4. And the reverse: what is the watering task part of?
curl "https://api.bottlebluellc.com/v1/links?to=bb:task:default:tsk_abc123" \
-H "x-api-key: $BBL_API_KEY"import requests
AQ = "https://api.aquabotany.com/v1"
BB = "https://api.bottlebluellc.com/v1"
H = {"x-api-key": API_KEY}
# 1. Create the three records exactly as you do today
event_id = requests.post(f"{AQ}/events", headers=H, json={
"title": "Spring planting day", "type": "gardening",
"description": "Get the beds in.", "datetime": "2026-09-01T09:00:00"}).json()["eventId"]
task_id = requests.post(f"{AQ}/tasks", headers=H, json={
"action_type": "water", "details": {"plant": "seedlings", "amount_ml": 500}}).json()["task_id"]
reminder_id = requests.post(f"{AQ}/tasks/reminders", headers=H, json={
"title": "Buy mulch", "due_at": "2026-08-30T08:00:00Z"}).json()["reminder"]["id"]
# 2. Connect them. The ids above become refs.
requests.post(f"{BB}/links", headers=H, json={
"from": f"bb:event:default:{event_id}",
"to": f"bb:task:default:{task_id}",
"type": "has_task",
})
requests.post(f"{BB}/links", headers=H, json={
"from": f"bb:event:default:{event_id}",
"to": f"bb:reminder:default:{reminder_id}",
"type": "has_reminder",
})
# 3. What belongs to Spring planting day? (the task and the reminder)
belongs = requests.get(f"{BB}/links", headers=H,
params={"from": f"bb:event:default:{event_id}"}).json()
# 4. And the reverse: what is the watering task part of?
parents = requests.get(f"{BB}/links", headers=H,
params={"to": f"bb:task:default:{task_id}"}).json()const AQ = "https://api.aquabotany.com/v1";
const BB = "https://api.bottlebluellc.com/v1";
const H = { "x-api-key": API_KEY, "Content-Type": "application/json" };
// 1. Create the three records exactly as you do today
const { eventId } = await (await fetch(`${AQ}/events`, {
method: "POST", headers: H,
body: JSON.stringify({ title: "Spring planting day", type: "gardening",
description: "Get the beds in.", datetime: "2026-09-01T09:00:00" })
})).json();
const { task_id } = await (await fetch(`${AQ}/tasks`, {
method: "POST", headers: H,
body: JSON.stringify({ action_type: "water", details: { plant: "seedlings", amount_ml: 500 } })
})).json();
const { reminder } = await (await fetch(`${AQ}/tasks/reminders`, {
method: "POST", headers: H,
body: JSON.stringify({ title: "Buy mulch", due_at: "2026-08-30T08:00:00Z" })
})).json();
const reminder_id = reminder.id;
// 2. Connect them. The ids above become refs.
await fetch(`${BB}/links`, {
method: "POST", headers: H,
body: JSON.stringify({
from: `bb:event:default:${eventId}`,
to: `bb:task:default:${task_id}`,
type: "has_task"
})
});
await fetch(`${BB}/links`, {
method: "POST", headers: H,
body: JSON.stringify({
from: `bb:event:default:${eventId}`,
to: `bb:reminder:default:${reminder_id}`,
type: "has_reminder"
})
});
// 3. What belongs to Spring planting day? (the task and the reminder)
const belongs = await (await fetch(
`${BB}/links?from=bb:event:default:${eventId}`, { headers: H })).json();
// 4. And the reverse: what is the watering task part of?
const parents = await (await fetch(
`${BB}/links?to=bb:task:default:${task_id}`, { headers: H })).json();Rules
- Both ends must be in the same platform. A link joins two records inside one namespace, and both refs must name the platform the request is scoped to. Anything else returns 400
CROSS_PLATFORM_LINK. This may be relaxed in a later version; the ref format already carries a platform, so nothing you write today would need to change. - Links are private to your account. They are stored under your user, so nobody else can read, traverse, or delete them, even knowing the exact refs.
- There are no duplicates and no link id. The
from+type+totriple is the identity, so sending the same link twice is safe and simply replays the first one with"created": false. - A record cannot link to itself: 400
SELF_LINK. - The wildcard is read-only.
platform=*reads links across every platform you have; writing to it returns 400WILDCARD_WRITE. - Deleting a record does not delete links pointing at it. Nothing checks a ref against its target. If the target is gone, following the ref returns 404 and you can delete the link yourself. This keeps a delete on any building block fast and predictable.
- Up to 500 links per account, after which a create returns 409
LIMIT_REACHED.
One call instead of two: ?expand=links
Every building block with a get-one-record endpoint supports the expand=links query parameter. Add it and the response carries a links object alongside the record, so reading a record and its relationships is one round trip instead of a read plus a /links query:
GET /notes/{note_id}?expand=links
{
"note_id": "9b1c2f6e-...",
"title": "Spring planting day prep",
...,
"links": {
"out": [ { "from": "bb:note:default:9b1c2f6e-...",
"to": "bb:task:default:abc123",
"type": "has_task",
"platform": "default",
"created_at": "2026-07-23T12:00:00" } ],
"in": [],
"truncated": false
}
}out rows are links FROM this record; in rows are links TO it; each row is the same shape GET /links returns. Three rules keep it predictable:
- Opt-in only. Without the parameter the response is exactly what it always was - no
linkskey appears, so existing clients see nothing new until they ask. - It is sugar, not a replacement. Each direction is capped at 50 rows and does not paginate.
"truncated": truemeans there are more; the full, paginated answer lives atGET /links?from=.../?to=.... - Scoped like everything else. Expansion is anchored on the record's own platform namespace and only ever returns your own links.
Supported on the get-one-record endpoints of /notes, /tasks, /list, /settings, /assets, /articles, /events, /challenges, /rewards, /actions, /leaderboard, /scoreboard, /streaks, and /workorder.
Market data
Base URL https://api.bottlebluellc.com/v1
Discovery index: every market and government dataset this API serves.
curl https://api.bottlebluellc.com/v1/market \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"market_categories": {
"bonds": { "category": "BOND_YIELD", "snapshot": "/market/bonds", "history": "/market/bonds/{entity}?start=&end=" },
"box-office": { "category": "BOX_OFFICE", "snapshot": "/market/box-office", "history": "/market/box-office/{entity}?start=&end=" },
"commodities": { "category": "COMMODITY", "snapshot": "/market/commodities", "history": "/market/commodities/{entity}?start=&end=" },
"cot": { "category": "COT", "snapshot": "/market/cot", "history": "/market/cot/{entity}?start=&end=" },
"crypto": { "category": "CRYPTO", "snapshot": "/market/crypto", "history": "/market/crypto/{entity}?start=&end=" },
"crypto-metrics": { "category": "CRYPTO_METRIC", "snapshot": "/market/crypto-metrics", "history": "/market/crypto-metrics/{entity}?start=&end=" },
"debt": { "category": "FISCAL", "snapshot": "/market/debt", "history": "/market/debt/{entity}?start=&end=" },
"drought": { "category": "DROUGHT", "snapshot": "/market/drought", "history": "/market/drought/{entity}?start=&end=" },
"econ": { "category": "ECON_INDICATOR", "snapshot": "/market/econ", "history": "/market/econ/{entity}?start=&end=" },
"energy": { "category": "ENERGY", "snapshot": "/market/energy", "history": "/market/energy/{entity}?start=&end=" },
"fx": { "category": "FX", "snapshot": "/market/fx", "history": "/market/fx/{entity}?start=&end=" },
"global-indices": { "category": "GLOBAL_INDEX", "snapshot": "/market/global-indices", "history": "/market/global-indices/{entity}?start=&end=" },
"indices": { "category": "INDEX", "snapshot": "/market/indices", "history": "/market/indices/{entity}?start=&end=" },
"metals": { "category": "METAL_PRICE", "snapshot": "/market/metals", "history": "/market/metals/{entity}?start=&end=" },
"rates": { "category": "INTEREST_RATE", "snapshot": "/market/rates", "history": "/market/rates/{entity}?start=&end=" },
"rig-count": { "category": "RIG_COUNT", "snapshot": "/market/rig-count", "history": "/market/rig-count/{entity}?start=&end=" },
"sectors": { "category": "SECTOR", "snapshot": "/market/sectors", "history": "/market/sectors/{entity}?start=&end=" },
"sentiment": { "category": "SENTIMENT", "snapshot": "/market/sentiment", "history": "/market/sentiment/{entity}?start=&end=" },
"spreads": { "category": "SPREAD", "snapshot": "/market/spreads", "history": "/market/spreads/{entity}?start=&end=" },
"weather": { "category": "WEATHER", "snapshot": "/market/weather", "history": "/market/weather/{entity}?start=&end=" }
},
"analytics": {
"stats": {
"all": "/market/stats",
"by_category": "/market/stats/{category} (category = any market slug, e.g. bonds)",
"by_entity": "/market/stats/{category}/{entity} e.g. /market/stats/bonds/^TNX",
"note": "per-instrument stats vs 5y of its own history; the date is the statement day, each row's 'latest_date' is the underlying data day. z-score / realized vol / momentum fields are null when undefined."
},
"earnings": {
"upcoming": "/market/earnings?days=N",
"by_ticker": "/market/earnings/{ticker}",
"note": "forward-looking: rows are written ahead of the report date, so this queries forward from today (default 8 days)."
}
},
"government_datasets": {
"13f-changes": "/government/13f-changes?year=&limit=",
"congress": "/government/congress?year=&limit=",
"contracts": "/government/contracts?year=&limit=",
"house": "/government/house?year=&limit=",
"insiders": "/government/insiders?year=&limit=",
"lobbying": "/government/lobbying?year=&limit=",
"off-exchange": "/government/off-exchange?year=&limit=",
"senate": "/government/senate?year=&limit=",
"wsb": "/government/wsb?year=&limit="
},
"government_analytics": {
"leaderboard": "/government/leaderboard?date=&limit=&chamber=",
"portfolio": "/government/portfolio/{chamber}/{name}?date=",
"note": "politician books + P&L, rebuilt daily at 6:30 PM ET; chamber is senate, house or executive; {name} must match the stored name, which the leaderboard lists."
},
"feeds": {
"news": "/market/news?source=&limit=",
"news_sources": ["MarketWatch", "Yahoo Finance", "Google News", "Google News Tech", "Government AI"],
"trending": "/market/trending?source=&limit=",
"trending_sources": ["Yahoo Finance", "pocketquant.io"],
"predictions": "/market/predictions?limit="
}
}Stored market headlines.
Most sources are scraped headlines with a link. The Government AI source is different: it is a daily AI-written summary of the previous day's government trading data. Its headline is the full post text, its topic names the day's dataset (e.g. Congress Trades), and it has no link (the summary is the content). Government AI rows appear once per posting day, Monday through Saturday.
| Parameter | In | Description |
|---|---|---|
source | query | optional Filter by source: MarketWatch, Yahoo Finance, Google News, Google News Tech, or Government AI. |
limit | query | optional Max items (default 25). |
curl https://api.bottlebluellc.com/v1/market/news \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/news",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/news", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/news", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"sources": [
"Government AI",
"pocketquant.io",
"..."
],
"count": 3,
"items": [
{
"source": "Government AI",
"timestamp": "2026-07-08T13:30:02.118392",
"headline": "Yesterday's congressional filings leaned into energy: four members disclosed new positions in XOM and CVX while two reported selling semiconductor holdings, a rotation worth watching against this week's crude strength.",
"topic": "Congress Trades"
},
{
"source": "pocketquant.io",
"timestamp": "2026-07-08T12:31:04.118392",
"headline": "Stocks edge higher as June CPI cools more than expected"
},
{
"source": "pocketquant.io",
"timestamp": "2026-07-08T11:58:47.902165",
"headline": "Fed officials signal patience on rate cuts ahead of FOMC minutes"
}
]
}Daily model-generated SPY predictions.
Items written after 2026-07-27 may also carry prediction_short, a short-form variant of the same day's call written for quick display, alongside the full prediction text. Older rows have only prediction.
curl https://api.bottlebluellc.com/v1/market/predictions \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/predictions",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/predictions", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/predictions", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"count": 2,
"items": [
{
"ticker": "SPY",
"timestamp": "2026-07-08T09:15:02.481932",
"prediction": "SPY is likely to drift modestly higher today. Futures point to a flat-to-positive open after a cooler June CPI print, 10-year yields eased to 4.18%, and breadth was constructive yesterday. Expect a range of roughly 618-624 with a close near 622, barring surprises in this afternoon's FOMC minutes.",
"prediction_short": "SPY leaning higher: cool CPI, easing yields, constructive breadth. Watching 618-624 with a close near 622."
},
{
"ticker": "SPY",
"timestamp": "2026-07-07T09:15:01.276854",
"prediction": "Expect SPY to trade sideways with a slight upward bias. Overnight futures are flat, crude is stable near $67, and there are no major data releases; a close between 619 and 623 looks most probable."
}
]
}The most talked about stocks across popular corners of the internet.
trending is the ranked, comma-separated ticker string. Items from source pocketquant.io written after 2026-07-27 also carry counts, the underlying tally behind that ranking: an object mapping each ticker to its mention count in the scan. Older rows and the Yahoo Finance source have only the ranked string.
curl https://api.bottlebluellc.com/v1/market/trending \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/trending",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/trending", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/trending", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"sources": [
"pocketquant.io"
],
"count": 2,
"items": [
{
"source": "pocketquant.io",
"timestamp": "2026-07-08T10:30:04.512874",
"trending": "NVDA, TSLA, PLTR, AMD, SMCI, HOOD, GME, SOFI",
"counts": { "NVDA": 23, "TSLA": 20, "PLTR": 14, "AMD": 11, "SMCI": 10, "HOOD": 9, "GME": 8, "SOFI": 8 }
},
{
"source": "pocketquant.io",
"timestamp": "2026-07-07T10:30:03.998131",
"trending": "TSLA, NVDA, PLTR, COIN, AMD, RDDT, GME, INTC"
}
]
}Latest daily snapshot for a market category.
| Parameter | In | Description |
|---|---|---|
category | path | required One of bonds, commodities, crypto, indices, global-indices, sectors, fx, rates, econ, metals, debt, crypto-metrics, sentiment, cot, energy, weather, drought, rig-count, box-office, spreads. The drought snapshot's coverage bands are cumulative (d0 means "D0 or worse", so they do not sum to 100) and include the DSCI index; box-office returns the weekend top-10 per-film rows plus a weekend-total row (see the worked example below); metals rows written after 2026-07-27 carry usd_per_unit inside price_data, the actual USD price (USD per troy ounce for XAU/XAG, USD per EUR for EUR), while price_in_usd is the raw units-per-USD rate kept under its historical name for compatibility, not the price; the spreads copper/gold ratio is a legitimately small number (about 0.0013), returned unrounded. |
date | query | optional YYYY-MM-DD; defaults to the latest available day. |
curl https://api.bottlebluellc.com/v1/market/crypto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/crypto",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/crypto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/crypto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"category": "crypto",
"date": "2026-07-08",
"count": 2,
"items": [
{
"date": "2026-07-08",
"entity": "BTC-USD",
"timestamp": "2026-07-08T13:35:12.408213Z",
"label": "Bitcoin",
"ticker": "BTC-USD",
"price": 118432.27,
"prev_close": 117210.55,
"change": 1221.72,
"change_pct": 1.04,
"source": "pocketquant.io"
},
{
"date": "2026-07-08",
"entity": "ETH-USD",
"timestamp": "2026-07-08T13:35:12.734569Z",
"label": "Ethereum",
"ticker": "ETH-USD",
"price": 4381.9,
"prev_close": 4402.15,
"change": -20.25,
"change_pct": -0.46,
"source": "pocketquant.io"
}
]
}Worked example: the box-office weekend
GET /market/box-office returns the most recent weekend's top-10 films, one row per film, plus one aggregate row derived from the weekend total. The snapshot date is the Sunday the weekend ends (rows are typically published early the following week). Per-film fields: rank, title, gross (this weekend's gross in USD), change_pct_lw (percent change vs last weekend, null for a debut), theaters, total_gross (running total for the release), weeks (weeks in release), distributor, and ticker, the distributor's listed parent company (null when the distributor has no listed parent, e.g. A24 or Angel). The aggregate row (entity TOTAL) carries the summed top-10 weekend gross as value plus label, units, and films. The example below shows the first two film rows and the total row; a real response has all ten films (count 11).
{
"category": "box-office",
"date": "2026-07-19",
"count": 11,
"items": [
{
"date": "2026-07-19",
"entity": "01",
"weekend_id": "2026W29",
"rank": 1,
"title": "The Odyssey",
"gross": 123502900,
"change_pct_lw": null,
"theaters": 3919,
"total_gross": 123502900,
"weeks": 1,
"distributor": "Universal Pictures",
"ticker": "CMCSA",
"timestamp": "2026-07-27T22:00:06.071117Z",
"source": "Bottle API"
},
{
"date": "2026-07-19",
"entity": "02",
"weekend_id": "2026W29",
"rank": 2,
"title": "Moana",
"gross": 17937618,
"change_pct_lw": -58.4,
"theaters": 4200,
"total_gross": 81019028,
"weeks": 2,
"distributor": "Walt Disney Studios Motion Pictures",
"ticker": "DIS",
"timestamp": "2026-07-27T22:00:06.553191Z",
"source": "Bottle API"
},
{
"date": "2026-07-19",
"entity": "TOTAL",
"weekend_id": "2026W29",
"label": "Top 10 Weekend Gross",
"value": 185866200,
"units": "USD",
"films": 10,
"timestamp": "2026-07-27T22:00:07.952037Z",
"source": "Bottle API"
}
]
}Historical time series for one instrument or series.
For box-office, the only chartable entity is TOTAL: GET /market/box-office/TOTAL charts the top-10 weekend total (value, USD), one point per weekend, dated the Sunday the weekend ends. Per-film ranks are not stable series, so individual films are not chartable; read them from the snapshot instead.
| Parameter | In | Description |
|---|---|---|
category | path | required A market category (see /market). |
entity | path | required Instrument/series id, e.g. ^TNX, BTC-USD, DFF. |
start | query | optional YYYY-MM-DD start of range. |
end | query | optional YYYY-MM-DD end of range (max 366 days). |
curl https://api.bottlebluellc.com/v1/market/crypto/BTC-USD \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/crypto/BTC-USD",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/crypto/BTC-USD", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/crypto/BTC-USD", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"category": "crypto",
"entity": "BTC-USD",
"start": "2026-06-08",
"end": "2026-07-08",
"count": 2,
"items": [
{
"date": "2026-07-07",
"entity": "BTC-USD",
"timestamp": "2026-07-07T13:35:11.106482Z",
"label": "Bitcoin",
"ticker": "BTC-USD",
"price": 117210.55,
"prev_close": 116004.9,
"change": 1205.65,
"change_pct": 1.04,
"source": "pocketquant.io"
},
{
"date": "2026-07-08",
"entity": "BTC-USD",
"timestamp": "2026-07-08T13:35:12.408213Z",
"label": "Bitcoin",
"ticker": "BTC-USD",
"price": 118432.27,
"prev_close": 117210.55,
"change": 1221.72,
"change_pct": 1.04,
"source": "pocketquant.io"
}
]
}Upcoming large-cap earnings (forward-looking).
Unlike every other market category, earnings rows are written ahead of their date (a scheduled report is not an observation), so this endpoint queries forward from today. Add /{ticker} for one symbol's next report, e.g. /market/earnings/AAPL.
| Parameter | In | Description |
|---|---|---|
days | query | optional Forward window in days (default 8, max 40). |
date | query | optional An exact day YYYY-MM-DD instead of the forward window. |
curl "https://api.bottlebluellc.com/v1/market/earnings?days=7" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/earnings",
headers={"x-api-key": API_KEY},
params={"days": 7},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/earnings?days=7", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/earnings?days=7", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"category": "earnings",
"from": "2026-07-13",
"to": "2026-07-20",
"count": 39,
"items": [
{
"date": "2026-07-15",
"entity": "JNJ",
"symbol": "JNJ",
"name": "Johnson and Johnson",
"report_date": "2026-07-15",
"time": "Before Open",
"market_cap": 400123000000,
"fiscal_quarter": "Jun/2026",
"eps_forecast": 2.68,
"num_estimates": "18",
"last_year_eps": 2.97
}
]
}One symbol's next scheduled earnings report in the forward window.
Same forward-looking window as /market/earnings, narrowed to one ticker. A symbol reports at most once in the window, so items holds either that one scheduled report or nothing; widen days (up to 40) if the next report is further out. The response echoes the symbol back as ticker.
| Parameter | In | Description |
|---|---|---|
ticker | path | required The symbol, e.g. AAPL. |
days | query | optional Forward window in days (default 8, max 40). |
date | query | optional An exact day YYYY-MM-DD instead of the forward window. |
curl "https://api.bottlebluellc.com/v1/market/earnings/AAPL?days=40" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/earnings/AAPL",
headers={"x-api-key": API_KEY},
params={"days": 40},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/earnings/AAPL?days=40", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/earnings/AAPL?days=40", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"category": "earnings",
"from": "2026-07-28",
"to": "2026-09-05",
"count": 1,
"ticker": "AAPL",
"items": [
{
"date": "2026-07-30",
"entity": "AAPL",
"symbol": "AAPL",
"name": "Apple Inc.",
"report_date": "2026-07-30",
"time": "After Close",
"market_cap": 4724334930960,
"fiscal_quarter": "Jun/2026",
"eps_forecast": 1.88,
"num_estimates": "11",
"last_year_eps": 1.57,
"timestamp": "2026-07-26T22:00:20.099706Z"
}
]
}Per-instrument statistics scored against 5 years of history, for every instrument at once.
Every instrument in the store is scored against its own 5-year history. This is the firehose form; narrow it with /market/stats/{category} (one source category) or /market/stats/{category}/{entity} (one instrument). The date is the day the statement was made; each row's latest_date is the underlying data day and may lag by a day or two. The zscore, realized_vol_30d and change_*_pct fields are null by design when undefined (a flat series has no z-score); they are not coerced to zero.
| Parameter | In | Description |
|---|---|---|
date | query | optional YYYY-MM-DD; defaults to the latest statement day. |
curl "https://api.bottlebluellc.com/v1/market/stats" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/stats",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"scope": "all",
"date": "2026-07-27",
"count": 104,
"items": [
{
"date": "2026-07-27",
"label": "US 10-Year Treasury",
"entity": "^TNX",
"source_category": "BOND_YIELD",
"latest": 4.647,
"latest_date": "2026-07-27",
"observations": 1258,
"mean": 3.7,
"percentile": 96.86,
"zscore": 1.0,
"realized_vol_30d": 13.88,
"change_12m_pct": 6.19,
"high_52w": 4.711,
"low_52w": 3.953,
"lookback_years": 5
},
{
"date": "2026-07-27",
"label": "US 30-Year Treasury",
"entity": "^TYX",
"source_category": "BOND_YIELD",
"latest": 5.125,
"latest_date": "2026-07-27",
"observations": 1258,
"mean": 4.01,
"percentile": 99.24,
"zscore": 1.18,
"realized_vol_30d": 11.49,
"change_12m_pct": 4.36,
"high_52w": 5.187,
"low_52w": 4.539,
"lookback_years": 5
}
]
}Per-instrument statistics for every instrument in one source category.
Same statement rows as /market/stats, filtered to one source category. The category path segment is a normal market slug; the response scopes itself to it. Items are abbreviated here; a real bonds response has all four Treasury series.
| Parameter | In | Description |
|---|---|---|
category | path | required A market slug: one of bonds, commodities, crypto, indices, global-indices, sectors, fx, rates, econ, metals, debt, crypto-metrics, sentiment, cot, energy, weather, drought, rig-count, box-office, spreads. |
date | query | optional YYYY-MM-DD; defaults to the latest statement day. |
curl "https://api.bottlebluellc.com/v1/market/stats/bonds" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/stats/bonds",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats/bonds", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats/bonds", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"scope": "bonds",
"date": "2026-07-27",
"count": 4,
"items": [
{
"date": "2026-07-27",
"label": "US 10-Year Treasury",
"entity": "^TNX",
"source_category": "BOND_YIELD",
"latest": 4.647,
"latest_date": "2026-07-27",
"observations": 1258,
"mean": 3.7,
"percentile": 96.86,
"zscore": 1.0,
"realized_vol_30d": 13.88,
"change_12m_pct": 6.19,
"high_52w": 4.711,
"low_52w": 3.953,
"lookback_years": 5
},
{
"date": "2026-07-27",
"label": "US 30-Year Treasury",
"entity": "^TYX",
"source_category": "BOND_YIELD",
"latest": 5.125,
"latest_date": "2026-07-27",
"observations": 1258,
"mean": 4.01,
"percentile": 99.24,
"zscore": 1.18,
"realized_vol_30d": 11.49,
"change_12m_pct": 4.36,
"high_52w": 5.187,
"low_52w": 4.539,
"lookback_years": 5
}
]
}The full statistics statement for one instrument.
The narrowest stats form returns one singular item rather than an items array, with the complete field set: distribution stats over the 5-year lookback (mean, stdev, min, max, percentile, zscore), momentum (change_1m_pct through change_12m_pct), volatility (realized_vol_30d, annualized from the last vol_window_observations observations), and the 52-week range with distances from it.
| Parameter | In | Description |
|---|---|---|
category | path | required A market slug (see /market/stats/{category} for the full list). |
entity | path | required An instrument id under that category, e.g. ^TNX. |
date | query | optional YYYY-MM-DD; defaults to the latest statement day. |
curl "https://api.bottlebluellc.com/v1/market/stats/bonds/^TNX" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/stats/bonds/^TNX",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats/bonds/^TNX", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/stats/bonds/^TNX", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"scope": "bonds",
"entity": "^TNX",
"date": "2026-07-27",
"item": {
"date": "2026-07-27",
"label": "US 10-Year Treasury",
"entity": "^TNX",
"source_category": "BOND_YIELD",
"latest": 4.647,
"latest_date": "2026-07-27",
"observations": 1258,
"lookback_years": 5,
"periods_per_year": 252,
"mean": 3.7001,
"stdev": 0.9494,
"min": 1.174,
"max": 4.988,
"percentile": 96.86,
"zscore": 1.0,
"realized_vol_30d": 13.88,
"vol_window_observations": 30,
"change_1m_pct": 4.03,
"change_3m_pct": 5.85,
"change_6m_pct": 9.94,
"change_12m_pct": 6.19,
"high_52w": 4.711,
"low_52w": 3.953,
"pct_from_high_52w": -1.36,
"pct_from_low_52w": 17.56,
"timestamp": "2026-07-27T23:00:16.490170Z"
}
}Government data
Base URL https://api.bottlebluellc.com/v1
US government financial-disclosure datasets.
| Parameter | In | Description |
|---|---|---|
dataset | path | required congress, senate, house, lobbying, contracts, off-exchange, insiders, wsb, or 13f-changes. |
year | query | optional 4-digit year (default current). |
limit | query | optional Max rows, 1 to 500. Defaults to 100 on insiders. |
ticker | query | optional insiders only. Returns just that symbol's filings. Case does not matter and surrounding spaces are ignored. Other datasets accept the parameter and ignore it, so the rows come back unfiltered. |
startKey | query | optional insiders only. The nextKey from your previous response, to fetch the next page. Other datasets ignore it. |
allYears | query | optional insiders only. Send true together with ticker to search every stored year instead of one. It has no effect on its own. |
Paging the insiders dataset. Every response carries truncated. When it is true, insiders also returns a nextKey: send that back as startKey to get the next page, and stop when truncated is false. Treat nextKey as an opaque string. It is bound to the query that produced it, so a key from one ticker or year cannot resume another and is refused with 400 invalid startKey.
curl https://api.bottlebluellc.com/v1/government/congress \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/government/congress",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/government/congress", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/government/congress", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"dataset": "congress",
"year": "2026",
"count": 2,
"items": [
{
"name": "Nancy Pelosi",
"timestamp": "2026-06-24",
"year": "2026",
"amount": 32500,
"report_date": "2026-07-02",
"last_modified": "2026-07-02",
"price_range": "$15,001 - $50,000",
"ticker": "NVDA",
"transaction": "Purchase",
"party": "D",
"house": "Representative"
},
{
"name": "Tommy Tuberville",
"timestamp": "2026-06-18",
"year": "2026",
"amount": 8000,
"report_date": "2026-06-30",
"last_modified": "2026-06-30",
"price_range": "$1,001 - $15,000",
"ticker": "XOM",
"transaction": "Sale",
"party": "R",
"house": "Senator"
}
],
"truncated": false
}Item shapes by dataset
The envelope (dataset, year, count, truncated, items) is the same for all nine datasets; the item fields differ per dataset. congress is shown above. The table and the sample items below give the shape of the other eight, one real row each. Numeric amounts are plain JSON numbers; year is a string everywhere except contracts, where it is a number.
| Dataset | Item fields |
|---|---|
senate | senator, bio_guide_id, ticker, transaction, amount, price_range, timestamp, year, last_modified |
house | name, ticker, transaction, amount, price_range, description, timestamp, year, last_modified |
lobbying | client, registrant, ticker, amount, issue, specific_issue, timestamp, year, filing_uuid, filing_period (issue and specific_issue may be null; filing_uuid and filing_period identify the Senate LDA filing and its reporting period, and are present on rows stored from 2026-09-09 onward) |
contracts | ticker, agency, description, amount, quarter, year, timestamp (older rows may lack agency/description) |
off-exchange | ticker, dpi (dark-pool indicator, the short share of OTC volume), otc_short, otc_total, timestamp, year |
insiders | name, position, ticker, transaction, shares, price, value, own_type, report_date, timestamp, year, acquired_disposed (acquired_disposed is "A" if the shares were acquired and "D" if they were disposed of, taken from the filing, and is present on rows stored from 2026-10-03 onward) |
wsb | ticker, rank, mentions, sentiment, timestamp, year (sentiment may be empty) |
13f-changes | owner, ticker, change, old_shares, new_shares, report_period, timestamp, year (report_period may be empty) |
{
"senate": {
"senator": "Sheldon Whitehouse",
"bio_guide_id": "W000802",
"ticker": "NVDA",
"transaction": "Sale (Partial)",
"amount": 15001,
"price_range": "$15,001 - $50,000",
"timestamp": "2026-06-30T00:00:00Z",
"year": "2026",
"last_modified": "2026-07-09"
},
"house": {
"name": "James A. Himes",
"ticker": "XOM",
"transaction": "Sale",
"amount": 15001,
"price_range": "$15,001 - $50,000",
"description": "N/A",
"timestamp": "2026-07-20T00:00:00Z",
"year": "2026",
"last_modified": "2026-07-21"
},
"lobbying": {
"client": "OCEANEERING INTERNATIONAL INC.",
"registrant": "IBEX2 SOLUTIONS, LLC",
"ticker": "OII",
"amount": 30000,
"issue": null,
"specific_issue": null,
"timestamp": "2026-09-09",
"year": "2026",
"filing_uuid": "ef6248b6-0c1e-4a52-9f3a-51d35e9e7c40",
"filing_period": "2nd Quarter (Apr 1 - June 30)"
},
"contracts": {
"ticker": "TDW",
"agency": "Department of State",
"description": "FUEL MANAGEMENT SYSTEM PM",
"amount": 16585,
"quarter": 3,
"year": 2026,
"timestamp": "2026-07-26"
},
"off-exchange": {
"ticker": "RIVN",
"dpi": 0.497587333917165,
"otc_short": 7595715,
"otc_total": 15265090,
"timestamp": "2026-07-24",
"year": "2026"
},
"insiders": {
"name": "Patel Snehal",
"position": "CEO and CFO",
"ticker": "GLSI",
"transaction": "Purchase",
"shares": 1100,
"price": 12.88,
"value": 14168,
"own_type": "Direct",
"report_date": "2026-07-23",
"timestamp": "2026-07-24",
"year": "2026",
"acquired_disposed": "A"
},
"wsb": {
"ticker": "AA",
"rank": 52,
"mentions": 3,
"sentiment": "",
"timestamp": "2026-07-27",
"year": "2026"
},
"13f-changes": {
"owner": "DT Investment Partners, LLC",
"ticker": "AGG",
"change": -9091,
"old_shares": 34507,
"new_shares": 25416,
"report_period": "",
"timestamp": "2026-07-27",
"year": "2026"
}
}Daily performance ranking of politician stock portfolios.
House and Senate members plus the president (chamber Executive) are ranked by portfolio return, one board per build day, rebuilt daily at 6:30 PM ET.
The inputs are public disclosures: congressional trade filings for the House and Senate, and the president's OGE Form 278e filings for the Executive chamber.
A politician is ranked only if they have deployed at least $10,000, have at least 3 disclosed trades, and have a disclosure within the last 730 days (~24 months) - this keeps a stale, no-longer-trading filer's old return from parking at the top of the board. Each row carries disclosed_trades (the count backing the ranking) and last_trade_date so a caller can see what a ranking rests on.
Each row also carries bioguide_id, the member's identifier in the Biographical Directory of the United States Congress, which is the stable way to join a row to an outside congressional dataset instead of matching on a name that filings spell inconsistently. It is optional, and the key is omitted rather than empty when there is no id: always for the Executive chamber, because the directory covers Congress only, and occasionally for a House or Senate row when a filing's spelling of a name cannot be matched to exactly one member.
Test for the key's presence rather than for a truthy value, and treat one id as identifying one person: a build never carries the same bioguide_id on two rows.
With no date the API serves the most recent build, walking back up to 7 days; a pinned date with no build that day returns an empty board (count 0) rather than an error, matching the market snapshot endpoints. With chamber the filter is applied before the limit cut, so ?chamber=&limit= means the top N of that chamber, not that chamber's survivors of the top N; the response then echoes the chamber back lowercased as chamber. truncated is true when limit cut the list.
A malformed date returns 400 date must be YYYY-MM-DD, an unknown chamber returns 400 with the valid chambers list, and when no build exists in the last 7 days the endpoint returns 404. Items are abbreviated below; a real board carries every ranked politician.
| Parameter | In | Description |
|---|---|---|
date | query | optional YYYY-MM-DD build date; defaults to the most recent build within 7 days. |
limit | query | optional Max rows (default 50). Out-of-range or non-numeric values are clamped or defaulted, never rejected. |
chamber | query | optional Filter to one chamber: senate, house, or executive (case-insensitive). |
# The full board, latest build
curl https://api.bottlebluellc.com/v1/government/leaderboard \
-H "x-api-key: $BBL_API_KEY"
# One chamber's top 10
curl "https://api.bottlebluellc.com/v1/government/leaderboard?chamber=senate&limit=10" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/government/leaderboard",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/government/leaderboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/government/leaderboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"dataset": "leaderboard",
"date": "2026-07-30",
"count": 2,
"truncated": false,
"items": [
{
"rank": 1,
"name": "David Perdue",
"chamber": "Senate",
"bioguide_id": "P000612",
"return_pct": 12.4,
"total_pnl": 184000.5,
"market_value": 1660000.0,
"total_invested": 1476000.0,
"open_positions": 14,
"disclosed_trades": 37,
"last_trade_date": "2026-07-22"
},
{
"rank": 2,
"name": "Josh Gottheimer",
"chamber": "House",
"bioguide_id": "G000583",
"return_pct": 9.8,
"total_pnl": 61250.0,
"market_value": 686500.0,
"total_invested": 625250.0,
"open_positions": 22,
"disclosed_trades": 18,
"last_trade_date": "2026-06-30"
}
]
}One politician's current book, positions marked to market.
The narrowest government-analytics form returns one singular item rather than an items array: the politician's open positions marked to market (ticker, shares, cost_basis, market_value, unrealized_pnl, unrealized_pct per position) plus the book-level totals (total_invested, market_value, realized_pnl, unrealized_pnl, total_pnl, return_pct, disclosed_trades, last_trade_date). Same daily 6:30 PM ET build as /government/leaderboard, and name must be the exact name as that board lists it; URL-encode spaces, e.g. /government/portfolio/executive/Donald%20J.%20Trump.
With no date the API serves the most recent build, walking back up to 7 days.
A malformed date returns 400 date must be YYYY-MM-DD, an unknown chamber returns 400 with the valid chambers list, and an unknown name returns 404 with a hint pointing at the leaderboard roster. Positions are abbreviated below; a real book lists all open_positions of them.
The item also carries bioguide_id on the same terms as the leaderboard row for the same politician, and the two always agree: the member's identifier in the Biographical Directory of the United States Congress, omitted rather than empty when there is no id, and never present for the Executive chamber.
| Parameter | In | Description |
|---|---|---|
chamber | path | required senate, house, or executive (case-insensitive). |
name | path | required The exact stored name as listed by the leaderboard, URL-encoded, e.g. David%20Perdue. |
date | query | optional YYYY-MM-DD build date; defaults to the most recent build within 7 days. |
curl "https://api.bottlebluellc.com/v1/government/portfolio/senate/David%20Perdue" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/government/portfolio/senate/David%20Perdue",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/government/portfolio/senate/David%20Perdue", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/government/portfolio/senate/David%20Perdue", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"dataset": "portfolio",
"chamber": "senate",
"name": "David Perdue",
"date": "2026-07-30",
"item": {
"name": "David Perdue",
"chamber": "Senate",
"bioguide_id": "P000612",
"positions": [
{
"ticker": "AAPL",
"shares": 120.0,
"cost_basis": 21400.0,
"market_value": 24800.0,
"unrealized_pnl": 3400.0,
"unrealized_pct": 15.9
},
{
"ticker": "JPM",
"shares": 85.0,
"cost_basis": 14875.0,
"market_value": 16320.0,
"unrealized_pnl": 1445.0,
"unrealized_pct": 9.7
}
],
"open_positions": 14,
"total_invested": 1476000.0,
"market_value": 1660000.0,
"realized_pnl": 52000.0,
"unrealized_pnl": 132000.5,
"total_pnl": 184000.5,
"return_pct": 12.4,
"disclosed_trades": 37,
"last_trade_date": "2026-07-22"
}
}WARN layoff notices, as published by state labor agencies, newest notice first.
| Parameter | In | Description |
|---|---|---|
state | query | required Two-letter state code. Case does not matter. A state with no notices returns an empty list rather than an error. |
from | query | optional Earliest notice date to return, as YYYY-MM-DD. Inclusive. |
to | query | optional Latest notice date to return, as YYYY-MM-DD. Inclusive. |
limit | query | optional Max rows, 1 to 500. Defaults to 100. |
startKey | query | optional The nextKey from your previous response, to fetch the next page. |
Dates are notice dates, not effective dates. notice_date is when the employer filed the notice and is what from, to and the ordering use. effective_date is when the separations take effect and is usually later. Filter on the one you mean.
Fields vary by state. Each agency publishes its own columns, so rows are returned as filed rather than flattened into one shape. state, notice_date, notice_id, employer and employees are on every row; location is city in some states and county or address in others. Every row carries its own source and source_url, so the attribution travels with the data.
Paging. Every response carries truncated. When it is true a nextKey is returned: send that back as startKey for the next page, and stop when truncated is false. Treat nextKey as an opaque string. It is bound to the query that produced it, so a key from one state or date cannot resume another and is refused with 400.
Beta. This endpoint is in beta. The response carries "beta": true so a client can detect it without reading this page. Coverage is still growing and the shape of a row may gain fields, so read fields by name and ignore ones you do not recognise. It will not lose the fields documented here while it is in beta.
curl "https://api.bottlebluellc.com/v1/labor/layoffs?state=CA&limit=50" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/labor/layoffs?state=CA&limit=50",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/labor/layoffs?state=CA&limit=50", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/labor/layoffs?state=CA&limit=50", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"dataset": "layoffs",
"beta": true,
"state": "CA",
"count": 1,
"truncated": true,
"items": [
{
"state": "CA",
"notice_date": "2026-10-06",
"effective_date": "2026-12-07",
"notice_id": "CA-d3e96c254ea7bd82",
"employer": "Beachbody, LLC",
"employees": "80",
"county": "Los Angeles County",
"address": "400 North Continental Blvd. El Segundo CA 90245",
"layoff_type": "Layoff Permanent",
"industry": "54 Professional Scientific and Technical Services",
"source": "California EDD",
"source_url": "https://edd.ca.gov/siteassets/files/jobs_and_training/warn/warn_report1.xlsx"
}
],
"nextKey": "eyJzdGF0ZSI6ICJDQSIsICJub3RpY2VfZGF0ZSI6ICIyMDI2LTEwLTA2In0"
}H-1B labor condition applications, as published by the US Department of Labor, for one decision date.
| Parameter | In | Description |
|---|---|---|
date | query | required The decision date to return, as YYYY-MM-DD. There is no default and no "latest": ask for the day you want. |
limit | query | optional Max rows, 1 to 500. Defaults to 100. |
startKey | query | optional The nextKey from your previous response, to fetch the next page. |
The source publishes quarterly, so most dates hold nothing. Applications arrive in a quarterly disclosure file and are grouped by the date the Department of Labor decided them. A date with no decisions returns an empty list, which is a normal answer and not an error. A busy decision date can hold several thousand applications, so page rather than assuming one response is the whole day.
No personal contact details are served. The published file includes contact names, emails and phone numbers for employer representatives and attorneys. Those columns are discarded when the data is collected, so they are not stored and cannot be returned. Every row carries its own source and source_file, so the attribution travels with the data.
Paging. Every response carries truncated. When it is true a nextKey is returned: send that back as startKey for the next page, and stop when truncated is false. Treat nextKey as an opaque string. It is bound to the query that produced it, so a key from one state or date cannot resume another and is refused with 400.
Beta. This endpoint is in beta. The response carries "beta": true so a client can detect it without reading this page. Coverage is still growing and the shape of a row may gain fields, so read fields by name and ignore ones you do not recognise. It will not lose the fields documented here while it is in beta.
curl "https://api.bottlebluellc.com/v1/labor/h1b?date=2026-06-26&limit=50" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/labor/h1b?date=2026-06-26&limit=50",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/labor/h1b?date=2026-06-26&limit=50", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/labor/h1b?date=2026-06-26&limit=50", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"dataset": "h1b",
"beta": true,
"date": "2026-06-26",
"count": 1,
"truncated": true,
"items": [
{
"CASE_NUMBER": "I-200-23124-443019",
"CASE_STATUS": "Certified - Withdrawn",
"VISA_CLASS": "H-1B",
"DECISION_DATE": "2026-06-26",
"RECEIVED_DATE": "2023-05-04",
"BEGIN_DATE": "2023-08-10",
"END_DATE": "2026-08-09",
"EMPLOYER_NAME": "Univ of WI System/Eau Claire",
"EMPLOYER_CITY": "Madison",
"EMPLOYER_STATE": "WI",
"EMPLOYER_FEIN": "39-6006492",
"JOB_TITLE": "Assistant Professor",
"SOC_CODE": "25-1121.00",
"SOC_TITLE": "Art, Drama, and Music Teachers, Postsecondary",
"FULL_TIME_POSITION": "Y",
"TOTAL_WORKER_POSITIONS": "1",
"WORKSITE_CITY": "Eau Claire",
"WORKSITE_STATE": "WI",
"WORKSITE_POSTAL_CODE": "54701",
"WAGE_RATE_OF_PAY_FROM": "56000",
"WAGE_UNIT_OF_PAY": "Year",
"PREVAILING_WAGE": "43780",
"PW_UNIT_OF_PAY": "Year",
"H_1B_DEPENDENT": "No",
"WILLFUL_VIOLATOR": "No",
"source": "DOL OFLC",
"source_file": "LCA_Disclosure_Data_FY2026_Q3.xlsx",
"source_fiscal_year": "2026",
"source_quarter": "3"
}
],
"nextKey": "eyJQSyI6ICJIMUJfTENBIzIwMjYtMDYtMjYiLCAiU0siOiAiQ0FTRSNJLTIwMC0yMzEyNC00NDMwMTkifQ"
}Utilities
Base URL https://api.bottlebluellc.com/v1
Practical calculator with ten categories: area, volume, geometry, construction & DIY, health & fitness, finance, statistics, probability, algebra, and unit-aware percentages. Browse every calculator type below.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'compound_interest', 'principal': 1000, 'rate': 0.05, 'time': 10}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 1628.89,
"meta": {
"category": "finance",
"formula_id": "finance:compound_interest"
}
}Every request has the same shape regardless of category: a type (one of the ten categories below), a params object specific to the kind you pick within that category, and an optional options object (precision, explain, and for finance:amortization_schedule also limit_rows and totals_only).
Show the formula: options.explain
Set explain: true to get the formula name and the exact inputs it used back in meta.explain, useful for showing your own users how a result was derived.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "bmr", "gender": "male", "weight_kg": 80, "height_cm": 178, "age": 32}, "options": {"precision": 2, "explain": true}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'bmr', 'gender': 'male', 'weight_kg': 80, 'height_cm': 178, 'age': 32}, 'options': {'precision': 2, 'explain': True}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bmr", "gender": "male", "weight_kg": 80, "height_cm": 178, "age": 32}, "options": {"precision": 2, "explain": true}})
});
const data = await resp.json();{
"result": 1757.5,
"meta": {
"category": "health",
"formula_id": "health:bmr",
"explain": {
"formula": "Mifflin-St Jeor",
"inputs": {
"kind": "bmr",
"gender": "male",
"weight_kg": 80,
"height_cm": 178,
"age": 32
}
}
}
}Batch mode: multiple calculations in one call
Send an array under batch instead of a single type/params/options object, up to 100 operations per request. Each item can override the top-level options. A failed item returns {"error": "..."} in its slot instead of failing the whole batch.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"batch": [{"type": "health", "params": {"kind": "bmi", "weight_kg": 75, "height_cm": 180}, "options": {"precision": 2}}, {"type": "geometry", "params": {"kind": "roof_pitch_angle", "rise": 6, "run": 12}, "options": {"precision": 2}}, {"type": "percentage", "params": {"kind": "tip", "amount": 64.5, "percent": 18}}]}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'batch': [{'type': 'health', 'params': {'kind': 'bmi', 'weight_kg': 75, 'height_cm': 180}, 'options': {'precision': 2}}, {'type': 'geometry', 'params': {'kind': 'roof_pitch_angle', 'rise': 6, 'run': 12}, 'options': {'precision': 2}}, {'type': 'percentage', 'params': {'kind': 'tip', 'amount': 64.5, 'percent': 18}}]},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"batch": [{"type": "health", "params": {"kind": "bmi", "weight_kg": 75, "height_cm": 180}, "options": {"precision": 2}}, {"type": "geometry", "params": {"kind": "roof_pitch_angle", "rise": 6, "run": 12}, "options": {"precision": 2}}, {"type": "percentage", "params": {"kind": "tip", "amount": 64.5, "percent": 18}}]})
});
const data = await resp.json();[
23.15,
26.57,
11.61
]No calculator types match your search.
No calculator types match your search.
Algebra3
Linear equations, 2x2 systems, and proportions.
linearSolve ax + b = c for x.a, b, c
Solve ax + b = c for x.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "algebra", "params": {"kind": "linear", "a": 3, "b": 5, "c": 20}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'algebra', 'params': {'kind': 'linear', 'a': 3, 'b': 5, 'c': 20}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "linear", "a": 3, "b": 5, "c": 20}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "linear", "a": 3, "b": 5, "c": 20}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 5.0,
"meta": {
"category": "algebra",
"formula_id": "algebra:linear"
}
}system_2x2Solve a 2x2 linear system with Cramer's rule.a1, b1, c1, a2, b2, c2
Solve a 2x2 linear system with Cramer's rule.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "algebra", "params": {"kind": "system_2x2", "a1": 2, "b1": 3, "c1": 13, "a2": 4, "b2": -1, "c2": 5}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'algebra', 'params': {'kind': 'system_2x2', 'a1': 2, 'b1': 3, 'c1': 13, 'a2': 4, 'b2': -1, 'c2': 5}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "system_2x2", "a1": 2, "b1": 3, "c1": 13, "a2": 4, "b2": -1, "c2": 5}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "system_2x2", "a1": 2, "b1": 3, "c1": 13, "a2": 4, "b2": -1, "c2": 5}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"x": 2.0,
"y": 3.0
},
"meta": {
"category": "algebra",
"formula_id": "algebra:system_2x2"
}
}proportionSolve the missing term in a/b = c/d.any 3 of a, b, c, d
Solve the missing term in a/b = c/d.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "algebra", "params": {"kind": "proportion", "a": 3, "b": 4, "d": 20}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'algebra', 'params': {'kind': 'proportion', 'a': 3, 'b': 4, 'd': 20}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "proportion", "a": 3, "b": 4, "d": 20}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "algebra", "params": {"kind": "proportion", "a": 3, "b": 4, "d": 20}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"c": 15.0
},
"meta": {
"category": "algebra",
"formula_id": "algebra:proportion"
}
}Area8
Flat-shape area formulas: rectangle, triangle, circle, trapezoid, ellipse, sector, segment, regular polygon.
rectangleRectangle area.length, width
Rectangle area.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "rectangle", "length": 12, "width": 8}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'rectangle', 'length': 12, 'width': 8}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "rectangle", "length": 12, "width": 8}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "rectangle", "length": 12, "width": 8}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 96,
"meta": {
"category": "area",
"formula_id": "area:rectangle"
}
}triangleTriangle area from base and height.base, height
Triangle area from base and height.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "triangle", "base": 10, "height": 6}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'triangle', 'base': 10, 'height': 6}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "triangle", "base": 10, "height": 6}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "triangle", "base": 10, "height": 6}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 30.0,
"meta": {
"category": "area",
"formula_id": "area:triangle"
}
}circleCircle area.radius
Circle area.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "circle", "radius": 4}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'circle', 'radius': 4}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "circle", "radius": 4}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "circle", "radius": 4}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 50.27,
"meta": {
"category": "area",
"formula_id": "area:circle"
}
}trapezoidTrapezoid area.base1, base2, height
Trapezoid area.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "trapezoid", "base1": 8, "base2": 12, "height": 5}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'trapezoid', 'base1': 8, 'base2': 12, 'height': 5}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "trapezoid", "base1": 8, "base2": 12, "height": 5}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "trapezoid", "base1": 8, "base2": 12, "height": 5}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 50.0,
"meta": {
"category": "area",
"formula_id": "area:trapezoid"
}
}ellipseEllipse area from semi-axes.a, b
Ellipse area from semi-axes.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "ellipse", "a": 6, "b": 3}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'ellipse', 'a': 6, 'b': 3}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "ellipse", "a": 6, "b": 3}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "ellipse", "a": 6, "b": 3}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 56.55,
"meta": {
"category": "area",
"formula_id": "area:ellipse"
}
}sectorCircular sector area.radius, angle_deg
Circular sector area.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "sector", "radius": 10, "angle_deg": 45}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'sector', 'radius': 10, 'angle_deg': 45}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "sector", "radius": 10, "angle_deg": 45}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "sector", "radius": 10, "angle_deg": 45}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 39.27,
"meta": {
"category": "area",
"formula_id": "area:sector"
}
}segmentCircular segment area.radius, angle_deg
Circular segment area.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "segment", "radius": 10, "angle_deg": 60}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'segment', 'radius': 10, 'angle_deg': 60}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "segment", "radius": 10, "angle_deg": 60}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "segment", "radius": 10, "angle_deg": 60}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 9.06,
"meta": {
"category": "area",
"formula_id": "area:segment"
}
}regular_polygonRegular polygon area from side count and side length.n, side
Regular polygon area from side count and side length.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "area", "params": {"shape": "regular_polygon", "n": 6, "side": 4}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'area', 'params': {'shape': 'regular_polygon', 'n': 6, 'side': 4}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "regular_polygon", "n": 6, "side": 4}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "area", "params": {"shape": "regular_polygon", "n": 6, "side": 4}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 41.57,
"meta": {
"category": "area",
"formula_id": "area:regular_polygon"
}
}Construction & DIY8
Job-site material takeoffs: board feet, concrete pours, drywall, paint, stairs, roofing bundles.
board_feetBoard feet of lumber.thickness_in, width_in, length_ft, quantity
Board feet of lumber.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "board_feet", "thickness_in": 2, "width_in": 6, "length_ft": 8, "quantity": 10}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'board_feet', 'thickness_in': 2, 'width_in': 6, 'length_ft': 8, 'quantity': 10}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "board_feet", "thickness_in": 2, "width_in": 6, "length_ft": 8, "quantity": 10}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "board_feet", "thickness_in": 2, "width_in": 6, "length_ft": 8, "quantity": 10}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 80.0,
"meta": {
"category": "construction",
"formula_id": "construction:board_feet"
}
}concrete_slabConcrete volume for a slab.length, width, thickness
Concrete volume for a slab.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "concrete_slab", "length": 20, "width": 15, "thickness": 0.33}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'concrete_slab', 'length': 20, 'width': 15, 'thickness': 0.33}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_slab", "length": 20, "width": 15, "thickness": 0.33}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_slab", "length": 20, "width": 15, "thickness": 0.33}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 99.0,
"meta": {
"category": "construction",
"formula_id": "construction:concrete_slab"
}
}concrete_footingConcrete volume for a footing.length, width, depth
Concrete volume for a footing.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "concrete_footing", "length": 30, "width": 1.5, "depth": 1}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'concrete_footing', 'length': 30, 'width': 1.5, 'depth': 1}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_footing", "length": 30, "width": 1.5, "depth": 1}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_footing", "length": 30, "width": 1.5, "depth": 1}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 45.0,
"meta": {
"category": "construction",
"formula_id": "construction:concrete_footing"
}
}concrete_sonotubeConcrete volume for cylindrical pier forms.diameter, height, quantity
Concrete volume for cylindrical pier forms.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "concrete_sonotube", "diameter": 1, "height": 4, "quantity": 6}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'concrete_sonotube', 'diameter': 1, 'height': 4, 'quantity': 6}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_sonotube", "diameter": 1, "height": 4, "quantity": 6}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "concrete_sonotube", "diameter": 1, "height": 4, "quantity": 6}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 18.85,
"meta": {
"category": "construction",
"formula_id": "construction:concrete_sonotube"
}
}drywall_sheetsSheets of drywall needed, rounded up.area, coverage_per_sheet
Sheets of drywall needed, rounded up.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "drywall_sheets", "area": 480, "coverage_per_sheet": 32}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'drywall_sheets', 'area': 480, 'coverage_per_sheet': 32}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "drywall_sheets", "area": 480, "coverage_per_sheet": 32}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "drywall_sheets", "area": 480, "coverage_per_sheet": 32}, "options": {}})
});
const data = await resp.json();{
"result": 15,
"meta": {
"category": "construction",
"formula_id": "construction:drywall_sheets"
}
}paint_gallonsGallons of paint needed.area, coverage_per_gallon
Gallons of paint needed.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "paint_gallons", "area": 900, "coverage_per_gallon": 350}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'paint_gallons', 'area': 900, 'coverage_per_gallon': 350}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "paint_gallons", "area": 900, "coverage_per_gallon": 350}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "paint_gallons", "area": 900, "coverage_per_gallon": 350}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 2.57,
"meta": {
"category": "construction",
"formula_id": "construction:paint_gallons"
}
}stairsStep count, run, and stringer length.total_rise, riser_height, tread_depth
Step count, run, and stringer length.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "stairs", "total_rise": 108, "riser_height": 7.5, "tread_depth": 10}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'stairs', 'total_rise': 108, 'riser_height': 7.5, 'tread_depth': 10}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "stairs", "total_rise": 108, "riser_height": 7.5, "tread_depth": 10}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "stairs", "total_rise": 108, "riser_height": 7.5, "tread_depth": 10}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"steps": 15,
"total_run": 150,
"stringer_length": 187.5
},
"meta": {
"category": "construction",
"formula_id": "construction:stairs"
}
}roofing_bundlesActual roof area adjusted for pitch, and bundles needed.roof_area_plan, pitch_rise, pitch_run, coverage_per_bundle
Actual roof area adjusted for pitch, and bundles needed.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "construction", "params": {"kind": "roofing_bundles", "roof_area_plan": 1800, "pitch_rise": 6, "pitch_run": 12, "coverage_per_bundle": 33.3}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'construction', 'params': {'kind': 'roofing_bundles', 'roof_area_plan': 1800, 'pitch_rise': 6, 'pitch_run': 12, 'coverage_per_bundle': 33.3}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "roofing_bundles", "roof_area_plan": 1800, "pitch_rise": 6, "pitch_run": 12, "coverage_per_bundle": 33.3}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "construction", "params": {"kind": "roofing_bundles", "roof_area_plan": 1800, "pitch_rise": 6, "pitch_run": 12, "coverage_per_bundle": 33.3}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"actual_area": 2012.46,
"bundles": 61
},
"meta": {
"category": "construction",
"formula_id": "construction:roofing_bundles"
}
}Finance10
Interest, loan payments, amortization, CAGR, margins, breakeven, and savings goals.
simple_interestSimple interest.principal, rate, time
Simple interest.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "simple_interest", "principal": 5000, "rate": 0.04, "time": 3}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'simple_interest', 'principal': 5000, 'rate': 0.04, 'time': 3}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "simple_interest", "principal": 5000, "rate": 0.04, "time": 3}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "simple_interest", "principal": 5000, "rate": 0.04, "time": 3}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 600.0,
"meta": {
"category": "finance",
"formula_id": "finance:simple_interest"
}
}compound_interestCompound interest with optional compounding frequency.principal, rate, time, n?
Compound interest with optional compounding frequency.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'compound_interest', 'principal': 1000, 'rate': 0.05, 'time': 10}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "compound_interest", "principal": 1000, "rate": 0.05, "time": 10}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 1628.89,
"meta": {
"category": "finance",
"formula_id": "finance:compound_interest"
}
}monthly_paymentFixed monthly loan payment.principal, annual_rate, years
Fixed monthly loan payment.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "monthly_payment", "principal": 250000, "annual_rate": 0.06, "years": 30}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'monthly_payment', 'principal': 250000, 'annual_rate': 0.06, 'years': 30}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "monthly_payment", "principal": 250000, "annual_rate": 0.06, "years": 30}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "monthly_payment", "principal": 250000, "annual_rate": 0.06, "years": 30}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 1498.88,
"meta": {
"category": "finance",
"formula_id": "finance:monthly_payment"
}
}amortization_scheduleFull or truncated amortization schedule.principal, annual_rate, years, limit_rows?, totals_only?
Full or truncated amortization schedule.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "amortization_schedule", "principal": 20000, "annual_rate": 0.07, "years": 3, "limit_rows": 3, "totals_only": false}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'amortization_schedule', 'principal': 20000, 'annual_rate': 0.07, 'years': 3, 'limit_rows': 3, 'totals_only': False}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "amortization_schedule", "principal": 20000, "annual_rate": 0.07, "years": 3, "limit_rows": 3, "totals_only": false}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "amortization_schedule", "principal": 20000, "annual_rate": 0.07, "years": 3, "limit_rows": 3, "totals_only": false}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"payment": 617.54,
"schedule": [
{
"month": 1,
"payment": 617.54,
"interest": 116.67,
"principal": 500.88,
"balance": 19499.12
},
{
"month": 2,
"payment": 617.54,
"interest": 113.74,
"principal": 503.8,
"balance": 18995.33
},
{
"month": 3,
"payment": 617.54,
"interest": 110.81,
"principal": 506.74,
"balance": 18488.59
}
]
},
"meta": {
"category": "finance",
"formula_id": "finance:amortization_schedule"
}
}extra_payment_impactMonths and interest saved by paying extra each month.principal, annual_rate, years, extra_per_month
Months and interest saved by paying extra each month.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "extra_payment_impact", "principal": 20000, "annual_rate": 0.07, "years": 5, "extra_per_month": 100}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'extra_payment_impact', 'principal': 20000, 'annual_rate': 0.07, 'years': 5, 'extra_per_month': 100}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "extra_payment_impact", "principal": 20000, "annual_rate": 0.07, "years": 5, "extra_per_month": 100}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "extra_payment_impact", "principal": 20000, "annual_rate": 0.07, "years": 5, "extra_per_month": 100}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"new_months": 47,
"saved_months": 13,
"saved_interest": 893.72
},
"meta": {
"category": "finance",
"formula_id": "finance:extra_payment_impact"
}
}cagrCompound annual growth rate.start_value, end_value, years
Compound annual growth rate.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "cagr", "start_value": 10000, "end_value": 18000, "years": 5}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'cagr', 'start_value': 10000, 'end_value': 18000, 'years': 5}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "cagr", "start_value": 10000, "end_value": 18000, "years": 5}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "cagr", "start_value": 10000, "end_value": 18000, "years": 5}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": 0.1247,
"meta": {
"category": "finance",
"formula_id": "finance:cagr"
}
}marginsGross and net margin as fractions of revenue.revenue, cogs, operating_expenses
Gross and net margin as fractions of revenue.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "margins", "revenue": 500000, "cogs": 300000, "operating_expenses": 100000}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'margins', 'revenue': 500000, 'cogs': 300000, 'operating_expenses': 100000}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "margins", "revenue": 500000, "cogs": 300000, "operating_expenses": 100000}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "margins", "revenue": 500000, "cogs": 300000, "operating_expenses": 100000}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": {
"gross_margin": 0.4,
"net_margin": 0.2
},
"meta": {
"category": "finance",
"formula_id": "finance:margins"
}
}breakevenBreakeven unit count and revenue.fixed_costs, price_per_unit, variable_cost_per_unit
Breakeven unit count and revenue.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "breakeven", "fixed_costs": 20000, "price_per_unit": 50, "variable_cost_per_unit": 30}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'breakeven', 'fixed_costs': 20000, 'price_per_unit': 50, 'variable_cost_per_unit': 30}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "breakeven", "fixed_costs": 20000, "price_per_unit": 50, "variable_cost_per_unit": 30}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "breakeven", "fixed_costs": 20000, "price_per_unit": 50, "variable_cost_per_unit": 30}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"units": 1000,
"revenue": 50000
},
"meta": {
"category": "finance",
"formula_id": "finance:breakeven"
}
}savings_goal (MONTHS)Required monthly contribution, or time to reach a savings target.target, months (solve monthly) or monthly (solve months), monthly_rate?
Required monthly contribution, or time to reach a savings target.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "months": 24, "monthly_rate": 0.004}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'savings_goal', 'target': 20000, 'months': 24, 'monthly_rate': 0.004}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "months": 24, "monthly_rate": 0.004}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "months": 24, "monthly_rate": 0.004}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 795.64,
"meta": {
"category": "finance",
"formula_id": "finance:savings_required_monthly"
}
}savings_goal (MONTHLY)Required monthly contribution, or time to reach a savings target.target, months (solve monthly) or monthly (solve months), monthly_rate?
Required monthly contribution, or time to reach a savings target.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "monthly": 750, "monthly_rate": 0.004}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'finance', 'params': {'kind': 'savings_goal', 'target': 20000, 'monthly': 750, 'monthly_rate': 0.004}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "monthly": 750, "monthly_rate": 0.004}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "finance", "params": {"kind": "savings_goal", "target": 20000, "monthly": 750, "monthly_rate": 0.004}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 25.39,
"meta": {
"category": "finance",
"formula_id": "finance:savings_time_to_goal"
}
}Geometry9
Right triangles, slope, roof pitch, circles, and oblique-triangle solving (law of sines / law of cosines).
pythagoreanHypotenuse from two legs.a, b
Hypotenuse from two legs.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "pythagorean", "a": 3, "b": 4}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'pythagorean', 'a': 3, 'b': 4}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "pythagorean", "a": 3, "b": 4}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "pythagorean", "a": 3, "b": 4}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 5.0,
"meta": {
"category": "geometry",
"formula_id": "geometry:pythagorean"
}
}slopeLine slope through two points.x1, y1, x2, y2
Line slope through two points.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "slope", "x1": 1, "y1": 2, "x2": 5, "y2": 14}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'slope', 'x1': 1, 'y1': 2, 'x2': 5, 'y2': 14}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "slope", "x1": 1, "y1": 2, "x2": 5, "y2": 14}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "slope", "x1": 1, "y1": 2, "x2": 5, "y2": 14}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 3.0,
"meta": {
"category": "geometry",
"formula_id": "geometry:slope"
}
}roof_pitch_angleRoof pitch as an angle in degrees.rise, run
Roof pitch as an angle in degrees.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "roof_pitch_angle", "rise": 6, "run": 12}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'roof_pitch_angle', 'rise': 6, 'run': 12}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "roof_pitch_angle", "rise": 6, "run": 12}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "roof_pitch_angle", "rise": 6, "run": 12}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 26.57,
"meta": {
"category": "geometry",
"formula_id": "geometry:roof_pitch_angle"
}
}circumferenceCircle circumference.radius
Circle circumference.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "circumference", "radius": 7}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'circumference', 'radius': 7}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "circumference", "radius": 7}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "circumference", "radius": 7}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 43.98,
"meta": {
"category": "geometry",
"formula_id": "geometry:circumference"
}
}arc_lengthArc length for a given central angle.radius, angle_deg
Arc length for a given central angle.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "arc_length", "radius": 7, "angle_deg": 90}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'arc_length', 'radius': 7, 'angle_deg': 90}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "arc_length", "radius": 7, "angle_deg": 90}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "arc_length", "radius": 7, "angle_deg": 90}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 11.0,
"meta": {
"category": "geometry",
"formula_id": "geometry:arc_length"
}
}law_of_sines_SSA (SSA)Solve a triangle given side-side-angle (may return an ambiguous-case pair).a, A, b or c
Solve a triangle given side-side-angle (may return an ambiguous-case pair).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "law_of_sines_SSA", "a": 10, "A": 30, "b": 14}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'law_of_sines_SSA', 'a': 10, 'A': 30, 'b': 14}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_sines_SSA", "a": 10, "A": 30, "b": 14}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_sines_SSA", "a": 10, "A": 30, "b": 14}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": [
{
"A": 30,
"B": 44.43,
"C": 105.57,
"a": 10,
"b": 14,
"c": 19.27
},
{
"A": 30,
"B": 135.57,
"C": 14.43,
"a": 10,
"b": 14,
"c": 4.98
}
],
"meta": {
"category": "geometry",
"formula_id": "geometry:law_of_sines"
}
}law_of_sines_ASA (ASA)Solve a triangle given angle-side-angle.A, B, one side
Solve a triangle given angle-side-angle.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "law_of_sines_ASA", "A": 40, "B": 60, "a": 12}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'law_of_sines_ASA', 'A': 40, 'B': 60, 'a': 12}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_sines_ASA", "A": 40, "B": 60, "a": 12}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_sines_ASA", "A": 40, "B": 60, "a": 12}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"A": 40,
"B": 60,
"C": 80.0,
"a": 12,
"b": 16.17,
"c": 18.39
},
"meta": {
"category": "geometry",
"formula_id": "geometry:law_of_sines"
}
}law_of_cosines (SSS)Solve a triangle's angles from three sides, or its third side from two sides and the included angle.a, b, c (SSS) or a, b, C (SAS)
Solve a triangle's angles from three sides, or its third side from two sides and the included angle.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "c": 9}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'law_of_cosines', 'a': 7, 'b': 8, 'c': 9}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "c": 9}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "c": 9}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"A": 48.19,
"B": 58.41,
"C": 73.4
},
"meta": {
"category": "geometry",
"formula_id": "geometry:law_of_cosines"
}
}law_of_cosines (SAS)Solve a triangle's angles from three sides, or its third side from two sides and the included angle.a, b, c (SSS) or a, b, C (SAS)
Solve a triangle's angles from three sides, or its third side from two sides and the included angle.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "C": 60}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'geometry', 'params': {'kind': 'law_of_cosines', 'a': 7, 'b': 8, 'C': 60}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "C": 60}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "geometry", "params": {"kind": "law_of_cosines", "a": 7, "b": 8, "C": 60}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": {
"c": 7.55
},
"meta": {
"category": "geometry",
"formula_id": "geometry:law_of_cosines"
}
}Health & fitness6
BMI, BMR, TDEE, calorie goals, Karvonen heart-rate zones, and body surface area.
bmiBody mass index.weight_kg, height_cm
Body mass index.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "bmi", "weight_kg": 75, "height_cm": 180}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'bmi', 'weight_kg': 75, 'height_cm': 180}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bmi", "weight_kg": 75, "height_cm": 180}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bmi", "weight_kg": 75, "height_cm": 180}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 23.15,
"meta": {
"category": "health",
"formula_id": "health:bmi"
}
}bmrBasal metabolic rate (Mifflin-St Jeor).gender, weight_kg, height_cm, age
Basal metabolic rate (Mifflin-St Jeor).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "bmr", "gender": "male", "weight_kg": 80, "height_cm": 178, "age": 32}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'bmr', 'gender': 'male', 'weight_kg': 80, 'height_cm': 178, 'age': 32}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bmr", "gender": "male", "weight_kg": 80, "height_cm": 178, "age": 32}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bmr", "gender": "male", "weight_kg": 80, "height_cm": 178, "age": 32}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 1757.5,
"meta": {
"category": "health",
"formula_id": "health:bmr"
}
}tdeeTotal daily energy expenditure from a BMR and an activity factor.bmr, activity_factor
Total daily energy expenditure from a BMR and an activity factor.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "tdee", "bmr": 1780, "activity_factor": 1.55}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'tdee', 'bmr': 1780, 'activity_factor': 1.55}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "tdee", "bmr": 1780, "activity_factor": 1.55}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "tdee", "bmr": 1780, "activity_factor": 1.55}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 2759.0,
"meta": {
"category": "health",
"formula_id": "health:tdee"
}
}calorie_goalDaily calorie target for cut, bulk, or maintain.tdee, goal
Daily calorie target for cut, bulk, or maintain.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "calorie_goal", "tdee": 2759, "goal": "cut"}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'calorie_goal', 'tdee': 2759, 'goal': 'cut'}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "calorie_goal", "tdee": 2759, "goal": "cut"}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "calorie_goal", "tdee": 2759, "goal": "cut"}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 2259,
"meta": {
"category": "health",
"formula_id": "health:calorie_goal"
}
}heart_rate_zonesKarvonen heart-rate training zones.age, resting_hr
Karvonen heart-rate training zones.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "heart_rate_zones", "age": 35, "resting_hr": 60}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'heart_rate_zones', 'age': 35, 'resting_hr': 60}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "heart_rate_zones", "age": 35, "resting_hr": 60}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "heart_rate_zones", "age": 35, "resting_hr": 60}, "options": {}})
});
const data = await resp.json();{
"result": {
"zone2": 135,
"zone3": 148,
"zone4": 160,
"zone5": 172,
"max_hr": 185
},
"meta": {
"category": "health",
"formula_id": "health:heart_rate_zones"
}
}bsa_mostellerBody surface area (Mosteller formula).height_cm, weight_kg
Body surface area (Mosteller formula).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "health", "params": {"kind": "bsa_mosteller", "height_cm": 180, "weight_kg": 80}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'health', 'params': {'kind': 'bsa_mosteller', 'height_cm': 180, 'weight_kg': 80}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bsa_mosteller", "height_cm": 180, "weight_kg": 80}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "health", "params": {"kind": "bsa_mosteller", "height_cm": 180, "weight_kg": 80}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 2.0,
"meta": {
"category": "health",
"formula_id": "health:bsa_mosteller"
}
}Percentage6
Percent change, discounts, tips, portions, and unit-aware bill splitting.
increasePercent increase.original, new
Percent increase.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "increase", "original": 80, "new": 100}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'increase', 'original': 80, 'new': 100}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "increase", "original": 80, "new": 100}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "increase", "original": 80, "new": 100}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 25.0,
"meta": {
"category": "percent",
"formula_id": "percent:increase"
}
}decreasePercent decrease.original, new
Percent decrease.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "decrease", "original": 100, "new": 80}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'decrease', 'original': 100, 'new': 80}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "decrease", "original": 100, "new": 80}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "decrease", "original": 100, "new": 80}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 20.0,
"meta": {
"category": "percent",
"formula_id": "percent:decrease"
}
}discountDiscounted price.price, percent
Discounted price.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "discount", "price": 89.99, "percent": 20}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'discount', 'price': 89.99, 'percent': 20}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "discount", "price": 89.99, "percent": 20}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "discount", "price": 89.99, "percent": 20}, "options": {}})
});
const data = await resp.json();{
"result": 71.99,
"meta": {
"category": "percent",
"formula_id": "percent:discount"
}
}tipTip amount.amount, percent
Tip amount.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "tip", "amount": 64.5, "percent": 18}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'tip', 'amount': 64.5, 'percent': 18}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "tip", "amount": 64.5, "percent": 18}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "tip", "amount": 64.5, "percent": 18}, "options": {}})
});
const data = await resp.json();{
"result": 11.61,
"meta": {
"category": "percent",
"formula_id": "percent:tip"
}
}portionValue of a percent of a total.total, percent
Value of a percent of a total.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "portion", "total": 250, "percent": 15}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'portion', 'total': 250, 'percent': 15}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "portion", "total": 250, "percent": 15}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "portion", "total": 250, "percent": 15}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 37.5,
"meta": {
"category": "percent",
"formula_id": "percent:portion"
}
}split_billTotal and even per-person share, tip and tax included.subtotal, tip_percent, tax_percent, people
Total and even per-person share, tip and tax included.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "percentage", "params": {"kind": "split_bill", "subtotal": 120, "tip_percent": 20, "tax_percent": 8.5, "people": 4}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'percentage', 'params': {'kind': 'split_bill', 'subtotal': 120, 'tip_percent': 20, 'tax_percent': 8.5, 'people': 4}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "split_bill", "subtotal": 120, "tip_percent": 20, "tax_percent": 8.5, "people": 4}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "percentage", "params": {"kind": "split_bill", "subtotal": 120, "tip_percent": 20, "tax_percent": 8.5, "people": 4}, "options": {}})
});
const data = await resp.json();{
"result": {
"total": 154.2,
"per_person": 38.55
},
"meta": {
"category": "percent",
"formula_id": "percent:split_bill"
}
}Probability6
Combinatorics and the normal / binomial distributions.
nCrCombinations (n choose r).n, r
Combinations (n choose r).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "nCr", "n": 10, "r": 3}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'nCr', 'n': 10, 'r': 3}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "nCr", "n": 10, "r": 3}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "nCr", "n": 10, "r": 3}, "options": {}})
});
const data = await resp.json();{
"result": 120,
"meta": {
"category": "probability",
"formula_id": "probability:nCr"
}
}nPrPermutations.n, r
Permutations.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "nPr", "n": 10, "r": 3}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'nPr', 'n': 10, 'r': 3}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "nPr", "n": 10, "r": 3}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "nPr", "n": 10, "r": 3}, "options": {}})
});
const data = await resp.json();{
"result": 720,
"meta": {
"category": "probability",
"formula_id": "probability:nPr"
}
}binomial_pmfBinomial probability mass function, P(X=k).n, k, p
Binomial probability mass function, P(X=k).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "binomial_pmf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'binomial_pmf', 'n': 10, 'k': 4, 'p': 0.3}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "binomial_pmf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "binomial_pmf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": 0.2001,
"meta": {
"category": "probability",
"formula_id": "probability:binomial_pmf"
}
}binomial_cdfBinomial cumulative distribution, P(X<=k).n, k, p
Binomial cumulative distribution, P(X<=k).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "binomial_cdf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'binomial_cdf', 'n': 10, 'k': 4, 'p': 0.3}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "binomial_cdf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "binomial_cdf", "n": 10, "k": 4, "p": 0.3}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": 0.8497,
"meta": {
"category": "probability",
"formula_id": "probability:binomial_cdf"
}
}normal_cdfNormal cumulative distribution.x, mean, stddev
Normal cumulative distribution.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "normal_cdf", "x": 68, "mean": 70, "stddev": 3}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'normal_cdf', 'x': 68, 'mean': 70, 'stddev': 3}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "normal_cdf", "x": 68, "mean": 70, "stddev": 3}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "normal_cdf", "x": 68, "mean": 70, "stddev": 3}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": 0.2525,
"meta": {
"category": "probability",
"formula_id": "probability:normal_cdf"
}
}normal_quantileInverse normal CDF (value at a percentile).p, mean, stddev
Inverse normal CDF (value at a percentile).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "probability", "params": {"kind": "normal_quantile", "p": 0.95, "mean": 100, "stddev": 15}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'probability', 'params': {'kind': 'normal_quantile', 'p': 0.95, 'mean': 100, 'stddev': 15}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "normal_quantile", "p": 0.95, "mean": 100, "stddev": 15}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "probability", "params": {"kind": "normal_quantile", "p": 0.95, "mean": 100, "stddev": 15}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 124.67,
"meta": {
"category": "probability",
"formula_id": "probability:normal_quantile"
}
}Statistics8
Descriptive statistics and simple linear regression over a data array.
meanArithmetic mean.data[]
Arithmetic mean.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "mean", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'mean', 'data': [12, 15, 9, 20, 14]}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "mean", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "mean", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 14.0,
"meta": {
"category": "stats",
"formula_id": "stats:mean"
}
}medianMedian.data[]
Median.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "median", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'median', 'data': [12, 15, 9, 20, 14]}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "median", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "median", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 14,
"meta": {
"category": "stats",
"formula_id": "stats:median"
}
}modeMode(s) and their frequency.data[]
Mode(s) and their frequency.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "mode", "data": [4, 4, 6, 7, 7, 7, 9]}, "options": {}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'mode', 'data': [4, 4, 6, 7, 7, 7, 9]}, 'options': {}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "mode", "data": [4, 4, 6, 7, 7, 7, 9]}, "options": {}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "mode", "data": [4, 4, 6, 7, 7, 7, 9]}, "options": {}})
});
const data = await resp.json();{
"result": {
"modes": [
7
],
"frequency": 3
},
"meta": {
"category": "stats",
"formula_id": "stats:mode"
}
}rangeRange (max minus min).data[]
Range (max minus min).
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "range", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'range', 'data': [12, 15, 9, 20, 14]}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "range", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "range", "data": [12, 15, 9, 20, 14]}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 11,
"meta": {
"category": "stats",
"formula_id": "stats:range"
}
}variance_stdVariance and standard deviation, sample or population.data[], sample?
Variance and standard deviation, sample or population.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "variance_std", "data": [12, 15, 9, 20, 14], "sample": true}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'variance_std', 'data': [12, 15, 9, 20, 14], 'sample': True}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "variance_std", "data": [12, 15, 9, 20, 14], "sample": true}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "variance_std", "data": [12, 15, 9, 20, 14], "sample": true}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": {
"variance": 16.5,
"stddev": 4.062
},
"meta": {
"category": "stats",
"formula_id": "stats:variance_std"
}
}z_scoreZ-score of a value.x, mean, stddev
Z-score of a value.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "z_score", "x": 82, "mean": 75, "stddev": 6}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'z_score', 'x': 82, 'mean': 75, 'stddev': 6}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "z_score", "x": 82, "mean": 75, "stddev": 6}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "z_score", "x": 82, "mean": 75, "stddev": 6}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 1.17,
"meta": {
"category": "stats",
"formula_id": "stats:z_score"
}
}percentile_from_zPercentile (CDF) for a z-score.z
Percentile (CDF) for a z-score.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "percentile_from_z", "z": 1.5}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'percentile_from_z', 'z': 1.5}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "percentile_from_z", "z": 1.5}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "percentile_from_z", "z": 1.5}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": 0.9332,
"meta": {
"category": "stats",
"formula_id": "stats:percentile_from_z"
}
}linear_regressionLeast-squares slope, intercept, and correlation.x[], y[]
Least-squares slope, intercept, and correlation.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "stats", "params": {"kind": "linear_regression", "x": [1, 2, 3, 4, 5], "y": [2.1, 4.0, 5.9, 8.1, 9.9]}, "options": {"precision": 4}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'stats', 'params': {'kind': 'linear_regression', 'x': [1, 2, 3, 4, 5], 'y': [2.1, 4.0, 5.9, 8.1, 9.9]}, 'options': {'precision': 4}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "linear_regression", "x": [1, 2, 3, 4, 5], "y": [2.1, 4.0, 5.9, 8.1, 9.9]}, "options": {"precision": 4}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "stats", "params": {"kind": "linear_regression", "x": [1, 2, 3, 4, 5], "y": [2.1, 4.0, 5.9, 8.1, 9.9]}, "options": {"precision": 4}})
});
const data = await resp.json();{
"result": {
"slope": 1.97,
"intercept": 0.09,
"r": 0.9996
},
"meta": {
"category": "stats",
"formula_id": "stats:linear_regression"
}
}Volume6
Solid-shape volume formulas: cube, rectangular prism, cylinder, sphere, cone, frustum cone.
cubeCube volume.side
Cube volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "cube", "side": 5}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'cube', 'side': 5}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cube", "side": 5}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cube", "side": 5}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 125,
"meta": {
"category": "volume",
"formula_id": "volume:cube"
}
}rectangular_prismRectangular prism (box) volume.length, width, height
Rectangular prism (box) volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "rectangular_prism", "length": 10, "width": 4, "height": 3}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'rectangular_prism', 'length': 10, 'width': 4, 'height': 3}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "rectangular_prism", "length": 10, "width": 4, "height": 3}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "rectangular_prism", "length": 10, "width": 4, "height": 3}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 120,
"meta": {
"category": "volume",
"formula_id": "volume:rectangular_prism"
}
}cylinderCylinder volume.radius, height
Cylinder volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "cylinder", "radius": 3, "height": 10}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'cylinder', 'radius': 3, 'height': 10}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cylinder", "radius": 3, "height": 10}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cylinder", "radius": 3, "height": 10}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 282.74,
"meta": {
"category": "volume",
"formula_id": "volume:cylinder"
}
}sphereSphere volume.radius
Sphere volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "sphere", "radius": 6}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'sphere', 'radius': 6}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "sphere", "radius": 6}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "sphere", "radius": 6}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 904.78,
"meta": {
"category": "volume",
"formula_id": "volume:sphere"
}
}coneCone volume.radius, height
Cone volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "cone", "radius": 4, "height": 9}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'cone', 'radius': 4, 'height': 9}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cone", "radius": 4, "height": 9}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "cone", "radius": 4, "height": 9}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 150.8,
"meta": {
"category": "volume",
"formula_id": "volume:cone"
}
}frustum_coneFrustum of a cone (truncated cone) volume.r1, r2, height
Frustum of a cone (truncated cone) volume.
curl https://api.bottlebluellc.com/v1/math \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "volume", "params": {"shape": "frustum_cone", "r1": 5, "r2": 3, "height": 8}, "options": {"precision": 2}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/math",
headers={"x-api-key": API_KEY},
json={'type': 'volume', 'params': {'shape': 'frustum_cone', 'r1': 5, 'r2': 3, 'height': 8}, 'options': {'precision': 2}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "frustum_cone", "r1": 5, "r2": 3, "height": 8}, "options": {"precision": 2}})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/math", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"type": "volume", "params": {"shape": "frustum_cone", "r1": 5, "r2": 3, "height": 8}, "options": {"precision": 2}})
});
const data = await resp.json();{
"result": 410.5,
"meta": {
"category": "volume",
"formula_id": "volume:frustum_cone"
}
}Time sync. No parameters. Returns utc_iso, epoch_s (float), epoch_ms (int), epoch_ns (int), lambda_request_id. Sends Cache-Control: no-store.
curl https://api.bottlebluellc.com/v1/ntp \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/ntp",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/ntp", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/ntp", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"utc_iso": "2026-07-07T12:34:56.789Z",
"epoch_s": 1752237296.789,
"epoch_ms": 1752237296789,
"epoch_ns": 1752237296789000000,
"lambda_request_id": "abc123-def456"
}Latest hourly random draw (256-bit), served in every stored format. A fresh draw is published each hour; responses carry a private, browser-only Cache-Control tuned to the next draw.
| Parameter | In | Description |
|---|---|---|
format | query | optional Return a single representation instead of all of them. One of uuid4, hex, base64url, integer, bits. |
curl https://api.bottlebluellc.com/v1/rng \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/rng",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/rng", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/rng", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"uuid4": "79ef559c-774f-4f46-80f5-f7921036ed97",
"hex": "9e1f5e43ebec47deb6f4450c934c4e10d9522a36525dc26adafe54d714655229",
"base64url": "nh9eQ-vsR9629EUMk0xOENlSKjZSXcJq2v5U1xRlUik",
"integer": "71520852930482833090887825723307774133283085537548162634608622221865433518633",
"bits": 256,
"timestamp": "2026-07-17T14:45:58.957846+00:00",
"type": "RngDraw",
"next_update_at": "2026-07-17T15:45:58.957846+00:00"
}Recent auto-generated poems (a daily creative feed).
curl https://api.bottlebluellc.com/v1/poetry \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/poetry",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/poetry", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/poetry", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"haikus": [
{
"timestamp": "2026-07-08T09:12:41.382910+00:00",
"haiku": "heat bends the sidewalk\na sprinkler ticks through the hush\nswallows stitch the dusk",
"headlines": [
"Heat advisory stretches into a fourth day across the Upper Midwest",
"City opens cooling centers as temperatures climb",
"Utilities urge conservation during evening peak"
]
},
{
"timestamp": "2026-07-07T09:11:58.104227+00:00",
"haiku": "rain maps the window\nmarkets wobble, then settle\na moth finds the light",
"headlines": [
"Stocks drift lower ahead of earnings season",
"Thunderstorms sweep the plains overnight",
"Transit ridership hits a post-pandemic high"
]
},
{
"timestamp": "2026-07-06T09:13:22.771034+00:00",
"haiku": "flags fold in the wind\nleftover fireworks crackle\nthe lake keeps its calm",
"headlines": [
"Holiday travel wraps up with few delays",
"Fireworks cleanup begins along the riverfront",
"Farmers welcome a stretch of dry weather"
]
}
],
"trends": {
"heat": 5,
"river": 4,
"storm": 3,
"market": 3,
"light": 2,
"summer": 2
},
"vibes": "Everyone is outside pretending the humidity is fine.",
"world_haiku": [
{
"timestamp": "2026-07-08T09:14:05.220481+00:00",
"haiku": "monsoon on the coast\nnegotiators break for tea\nsomewhere, a bell rings",
"date": "July 08, 2026",
"headlines": [
"Monsoon rains arrive early across South Asia",
"Trade talks resume in Geneva",
"Global heat record tied for June"
]
},
{
"timestamp": "2026-07-07T09:13:47.918356+00:00",
"haiku": "old ice calves at dawn\nheadlines argue over waves\ngulls vote with their wings",
"date": "July 07, 2026",
"headlines": [
"Glacier study revises sea-level forecasts",
"Shipping lanes reroute around storm system",
"Summit ends without a joint statement"
]
}
],
"world_vibe": "The world is holding meetings about the weather it caused.",
"world_trends": {
"monsoon": 4,
"summit": 3,
"coast": 3,
"ice": 2,
"trade": 2
}
}Milliseconds remaining until local midnight.
curl https://api.bottlebluellc.com/v1/seconds/midnight?timezone=UTC \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/seconds/midnight?timezone=UTC",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/midnight?timezone=UTC", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/midnight?timezone=UTC", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"timezone": "UTC",
"remainingMilliseconds": 16070000
}The current astronomical season.
curl https://api.bottlebluellc.com/v1/seconds/season \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/seconds/season",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/season", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/season", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"date": "2026-07-08",
"season": "Summer"
}Milliseconds remaining until sunset.
curl https://api.bottlebluellc.com/v1/seconds/sunset \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/seconds/sunset",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/sunset", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/seconds/sunset", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"millisecondsUntilSunset": 23390000,
"current_time": "2026-07-08 14:32:10",
"sunset_time": "2026-07-08 21:02:00"
}Convert a value between units of measure. Send the parameters as a JSON body (the endpoint reads the request body, and CloudFront rejects a GET that carries one).
| Parameter | In | Description |
|---|---|---|
value | body | required Numeric value to convert. |
from | body | required Source unit code, e.g. km. |
to | body | required Target unit code, e.g. mi. |
curl https://api.bottlebluellc.com/v1/units \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": 100, "from": "km", "to": "mi"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/units",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={'value': 100, 'from': 'km', 'to': 'mi'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/units", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": 100, "from": "km", "to": "mi"})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/units", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": 100, "from": "km", "to": "mi"})
});
const data = await resp.json();{
"input": {
"value": 100,
"from": "km",
"to": "mi"
},
"result": 62.137273664980675,
"formula": "100 * 1000.0 / 1609.34"
}Notes
Base URL https://api.bottlebluellc.com/v1
List the caller's notes.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/notes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/notes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/notes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"note_id": "9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
"title": "Ideas",
"content": "Plant tomatoes in May",
"created_at": "2026-07-08T09:15:42.123456",
"updated_at": "2026-07-08T09:15:42.123456"
},
{
"note_id": "5d8e7a94-2c1b-4f6d-a3e9-8b4c6d2f1a70",
"title": "Watering schedule",
"content": "Basil every 2 days, rosemary weekly",
"created_at": "2026-07-07T18:02:11.904312",
"updated_at": "2026-07-08T07:41:05.238771"
}
]
}Create a note.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Ideas", "content": "Plant tomatoes in May"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/notes \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Ideas", "content": "Plant tomatoes in May", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/notes",
headers={"x-api-key": API_KEY},
json={'title': 'Ideas', 'content': 'Plant tomatoes in May'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/notes",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'title': 'Ideas', 'content': 'Plant tomatoes in May', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May", "platform": "my_garden"})
});{
"message": "Note created",
"note_id": "9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21"
}Get a single note. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"note_id": "9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
"title": "Ideas",
"content": "Plant tomatoes in May",
"created_at": "2026-07-08T09:15:42.123456",
"updated_at": "2026-07-08T09:15:42.123456"
}Update a note.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
json={'title': 'Ideas', 'content': 'Plant tomatoes in May, start seeds indoors in April'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'title': 'Ideas', 'content': 'Plant tomatoes in May, start seeds indoors in April', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"title": "Ideas", "content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"})
});{
"message": "Note updated"
}Update a note (the handler treats PATCH like PUT).
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Plant tomatoes in May, start seeds indoors in April"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
json={'content': 'Plant tomatoes in May, start seeds indoors in April'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'content': 'Plant tomatoes in May, start seeds indoors in April', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"content": "Plant tomatoes in May, start seeds indoors in April"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"content": "Plant tomatoes in May, start seeds indoors in April"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"content": "Plant tomatoes in May, start seeds indoors in April", "platform": "my_garden"})
});{
"message": "Note updated"
}Delete a note.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/notes/9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Note deleted"
}Links
Base URL https://api.bottlebluellc.com/v1 (also served on https://api.aquabotany.com/v1)
List links. Pass from to see what a record points at, or to to see what points at it. Also available on api.aquabotany.com.
| Parameter | In | Description |
|---|---|---|
from | query | optional Ref of the record to traverse forward from. |
to | query | optional Ref of the record to traverse backward from. Cannot be combined with from. |
type | query | optional Only return links with this relationship verb. |
platform | query | optional Namespace to read. Omitted means your default namespace; * reads across every platform you have. |
limit | query | optional Max results (default 50, max 200). |
next_token | query | optional Opaque cursor from a previous response. |
# What does this event own?
curl "https://api.bottlebluellc.com/v1/links?from=bb:event:default:xyz789" \
-H "x-api-key: $BBL_API_KEY"
# What is this task part of?
curl "https://api.bottlebluellc.com/v1/links?to=bb:task:default:abc123" \
-H "x-api-key: $BBL_API_KEY"
# Narrow to one relationship, or read across every platform
curl "https://api.bottlebluellc.com/v1/links?from=bb:event:default:xyz789&type=has_task" \
-H "x-api-key: $BBL_API_KEY"
curl "https://api.bottlebluellc.com/v1/links?platform=*" \
-H "x-api-key: $BBL_API_KEY"import requests
BB = "https://api.bottlebluellc.com/v1/links"
H = {"x-api-key": API_KEY}
# What does this event own?
owned = requests.get(BB, headers=H,
params={"from": "bb:event:default:xyz789"}).json()
# What is this task part of?
parents = requests.get(BB, headers=H,
params={"to": "bb:task:default:abc123"}).json()
# Narrow to one relationship, or read across every platform
tasks_only = requests.get(BB, headers=H, params={
"from": "bb:event:default:xyz789", "type": "has_task"}).json()
everywhere = requests.get(BB, headers=H, params={"platform": "*"}).json()const BB = "https://api.bottlebluellc.com/v1/links";
const H = { "x-api-key": API_KEY };
// What does this event own?
const owned = await (await fetch(
`${BB}?from=bb:event:default:xyz789`, { headers: H })).json();
// What is this task part of?
const parents = await (await fetch(
`${BB}?to=bb:task:default:abc123`, { headers: H })).json();
// Narrow to one relationship, or read across every platform
const tasksOnly = await (await fetch(
`${BB}?from=bb:event:default:xyz789&type=has_task`, { headers: H })).json();
const everywhere = await (await fetch(`${BB}?platform=*`, { headers: H })).json();const BB = "https://api.bottlebluellc.com/v1/links";
const H = { "x-api-key": API_KEY };
// What does this event own?
const owned = await (await fetch(
`${BB}?from=bb:event:default:xyz789`, { headers: H })).json();
// What is this task part of?
const parents = await (await fetch(
`${BB}?to=bb:task:default:abc123`, { headers: H })).json();
// Narrow to one relationship, or read across every platform
const tasksOnly = await (await fetch(
`${BB}?from=bb:event:default:xyz789&type=has_task`, { headers: H })).json();
const everywhere = await (await fetch(`${BB}?platform=*`, { headers: H })).json();{
"links": [
{
"from": "bb:event:default:xyz789",
"to": "bb:task:default:abc123",
"type": "has_task",
"platform": "default",
"created_at": "2026-07-19T14:02:11.482913"
},
{
"from": "bb:event:default:xyz789",
"to": "bb:reminder:default:r55",
"type": "has_reminder",
"platform": "default",
"created_at": "2026-07-19T14:02:12.113044"
}
]
}Link two records together. Also available on api.aquabotany.com.
| Parameter | In | Description |
|---|---|---|
from | body | required Ref of the record the link starts at. |
to | body | required Ref of the record the link points at. |
type | body | optional Relationship verb, your own vocabulary (letters, digits, and _.- , max 64). Defaults to related. |
platform | body | optional Namespace both refs must name. Defaults to default; * is not allowed on writes. |
curl https://api.bottlebluellc.com/v1/links \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from": "bb:event:default:xyz789", "to": "bb:task:default:abc123", "type": "has_task"}'
# Scoped to one platform (both refs must name the same one)
curl https://api.bottlebluellc.com/v1/links \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from": "bb:event:my_garden:xyz789", "to": "bb:task:my_garden:abc123", "type": "has_task", "platform": "my_garden"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/links",
headers={"x-api-key": API_KEY},
json={
"from": "bb:event:default:xyz789",
"to": "bb:task:default:abc123",
"type": "has_task",
},
)
print(resp.status_code, resp.json())
# Scoped to one platform (both refs must name the same one)
resp = requests.post(
"https://api.bottlebluellc.com/v1/links",
headers={"x-api-key": API_KEY},
json={
"from": "bb:event:my_garden:xyz789",
"to": "bb:task:my_garden:abc123",
"type": "has_task",
"platform": "my_garden",
},
)const resp = await fetch("https://api.bottlebluellc.com/v1/links", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
from: "bb:event:default:xyz789",
to: "bb:task:default:abc123",
type: "has_task"
})
});
const data = await resp.json();
// Scoped to one platform (both refs must name the same one)
const scoped = await fetch("https://api.bottlebluellc.com/v1/links", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
from: "bb:event:my_garden:xyz789",
to: "bb:task:my_garden:abc123",
type: "has_task",
platform: "my_garden"
})
});const resp = await fetch("https://api.bottlebluellc.com/v1/links", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
from: "bb:event:default:xyz789",
to: "bb:task:default:abc123",
type: "has_task"
})
});
const data = await resp.json();
// Scoped to one platform (both refs must name the same one)
const scoped = await fetch("https://api.bottlebluellc.com/v1/links", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
from: "bb:event:my_garden:xyz789",
to: "bb:task:my_garden:abc123",
type: "has_task",
platform: "my_garden"
})
});{
"message": "Link created",
"created": true,
"link": {
"from": "bb:event:default:xyz789",
"to": "bb:task:default:abc123",
"type": "has_task",
"platform": "default",
"created_at": "2026-07-19T14:02:11.482913"
}
}Remove one link. The records themselves are untouched. Also available on api.aquabotany.com.
| Parameter | In | Description |
|---|---|---|
from | query | required Ref the link starts at. |
to | query | required Ref the link points at. |
type | query | optional Relationship verb. Defaults to related, so pass the same value you created the link with. |
platform | query | optional Namespace the link lives in. Omitted means your default namespace. |
curl "https://api.bottlebluellc.com/v1/links?from=bb:event:default:xyz789&to=bb:task:default:abc123&type=has_task" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.delete(
"https://api.bottlebluellc.com/v1/links",
headers={"x-api-key": API_KEY},
params={
"from": "bb:event:default:xyz789",
"to": "bb:task:default:abc123",
"type": "has_task",
},
)
print(resp.status_code, resp.json())const params = new URLSearchParams({
from: "bb:event:default:xyz789",
to: "bb:task:default:abc123",
type: "has_task"
});
const resp = await fetch(`https://api.bottlebluellc.com/v1/links?${params}`, {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const params = new URLSearchParams({
from: "bb:event:default:xyz789",
to: "bb:task:default:abc123",
type: "has_task"
});
const resp = await fetch(`https://api.bottlebluellc.com/v1/links?${params}`, {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"message": "Link deleted"
}Lists
Base URL https://api.bottlebluellc.com/v1
The list lifecycle
A list is a named container; its entries are separate item records that point back at it. You build one in two steps - create the list, then add its items - so a full walkthrough is: (1) create a list and keep the list_id it returns, (2) add items with that same list_id, and (3) read the list back with its items attached. The per-operation reference for each call follows below.
# 1. Create a list. Only "name" is required; "description" is optional.
curl https://api.bottlebluellc.com/v1/list \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Spring shopping", "description": "For planting day"}'
# -> {"status": "created", "list_id": "lst_3f2c9a", "name": "Spring shopping", "platform": "default"}
# 2. Add items, reusing the list_id from step 1. One item per call; only "value" is required.
curl https://api.bottlebluellc.com/v1/list/lst_3f2c9a/item \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Potting soil", "notes": "2 bags"}'
# -> {"status": "item added", "item_id": "itm_c4a8f1", "platform": "default"}
curl https://api.bottlebluellc.com/v1/list/lst_3f2c9a/item \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Mulch"}'
# 3. Read the list back, its items included.
curl https://api.bottlebluellc.com/v1/list/lst_3f2c9a \
-H "x-api-key: $BBL_API_KEY"import requests
BB = "https://api.bottlebluellc.com/v1"
H = {"x-api-key": API_KEY}
# 1. Create a list. Only "name" is required; "description" is optional.
list_id = requests.post(f"{BB}/list", headers=H, json={
"name": "Spring shopping", "description": "For planting day"}).json()["list_id"]
# 2. Add items, reusing list_id. One item per call; only "value" is required.
for value in ["Potting soil", "Mulch"]:
requests.post(f"{BB}/list/{list_id}/item", headers=H, json={"value": value})
# 3. Read the list back, its items included.
detail = requests.get(f"{BB}/list/{list_id}", headers=H).json()
# detail["items"] -> [{"item_id": ..., "value": "Potting soil", "notes": "2 bags", ...}, ...]const BB = "https://api.bottlebluellc.com/v1";
const H = { "x-api-key": API_KEY, "Content-Type": "application/json" };
// 1. Create a list. Only "name" is required; "description" is optional.
const { list_id } = await (await fetch(`${BB}/list`, {
method: "POST", headers: H,
body: JSON.stringify({ name: "Spring shopping", description: "For planting day" })
})).json();
// 2. Add items, reusing list_id. One item per call; only "value" is required.
for (const value of ["Potting soil", "Mulch"]) {
await fetch(`${BB}/list/${list_id}/item`, {
method: "POST", headers: H, body: JSON.stringify({ value })
});
}
// 3. Read the list back, its items included.
const detail = await (await fetch(`${BB}/list/${list_id}`, { headers: H })).json();There is no create-with-items shortcut. POST /list only accepts name and description; items are always added one at a time with POST /list/{listId}/item. The list must exist first - adding an item to an unknown list_id returns 404. Lists and their items are capped per account (a create past the cap returns 409 LIMIT_REACHED).
Named lists (to-do, shopping, etc.).
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/list?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/list",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/list",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
"name": "Shopping",
"description": null,
"created_at": "2026-07-08T09:20:31.482910+00:00",
"updated_at": "2026-07-08T09:20:31.482910+00:00"
},
{
"list_id": "b7e1d4c8-05f2-49a6-8d3b-2e9c7a1f6540",
"name": "Garden to-do",
"description": "Spring planting tasks",
"created_at": "2026-07-06T15:44:09.117203+00:00",
"updated_at": "2026-07-07T11:02:54.660318+00:00"
}
]Named lists (to-do, shopping, etc.).
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Shopping"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/list \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Shopping", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/list",
headers={"x-api-key": API_KEY},
json={'name': 'Shopping'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/list",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'name': 'Shopping', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Shopping"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"name": "Shopping", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Shopping"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"name": "Shopping", "platform": "my_garden"})
});{
"status": "created",
"list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
"name": "Shopping"
}Read or delete a single list. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
"name": "Shopping",
"description": null,
"items": [
{
"item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
"value": "Potting soil",
"notes": null,
"created_at": "2026-07-08T09:25:07.318842+00:00",
"updated_at": "2026-07-08T09:25:07.318842+00:00"
}
]
}Read or delete a single list.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "list deleted",
"list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
"deleted": 2
}Add an item to a list.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Potting soil"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Potting soil", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item",
headers={"x-api-key": API_KEY},
json={'value': 'Potting soil'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'value': 'Potting soil', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": "Potting soil"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"value": "Potting soil", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": "Potting soil"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"value": "Potting soil", "platform": "my_garden"})
});{
"status": "item added",
"item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}Update or remove a list item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Rosemary plant", "notes": "Move to sunlight"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"value": "Rosemary plant", "notes": "Move to sunlight", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
json={'value': 'Rosemary plant', 'notes': 'Move to sunlight'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'value': 'Rosemary plant', 'notes': 'Move to sunlight', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": "Rosemary plant", "notes": "Move to sunlight"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"value": "Rosemary plant", "notes": "Move to sunlight", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"value": "Rosemary plant", "notes": "Move to sunlight"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"value": "Rosemary plant", "notes": "Move to sunlight", "platform": "my_garden"})
});{
"status": "updated",
"item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}Update or remove a list item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notes": "Move to sunlight"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notes": "Move to sunlight", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
json={'notes': 'Move to sunlight'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'notes': 'Move to sunlight', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"notes": "Move to sunlight"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"notes": "Move to sunlight", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"notes": "Move to sunlight"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"notes": "Move to sunlight", "platform": "my_garden"})
});{
"status": "updated",
"item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}Update or remove a list item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/list/3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42/item/c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted",
"item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}Queues
Base URL https://api.bottlebluellc.com/v1
Read a queue's contents, or purge it.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/queue/deploys",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/queue/deploys",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"queue_id": "QUEUE_ID",
"items": [
{
"queue_id": "QUEUE_ID",
"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
"user_id": "u123",
"priority": 100,
"payload": {
"any": "json"
},
"state": "pending",
"created_at": "2026-07-08T14:02:11.517301+00:00",
"claimed_at": null,
"lease_until": null,
"claimed_by": null
},
{
"queue_id": "QUEUE_ID",
"item_id": "7d4c2b19-5e8f-4a06-b3d2-91c07e5a48f6",
"user_id": "u456",
"priority": 100,
"payload": {
"any": "json"
},
"state": "claimed",
"created_at": "2026-07-08T13:55:40.102938+00:00",
"claimed_at": "2026-07-08T14:01:02.884211+00:00",
"lease_until": "2026-07-08T14:02:02.884211+00:00",
"claimed_by": "u_9f3a1c7b8e2d0a4f"
}
]
}Read a queue's contents, or purge it.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/queue/deploys",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/queue/deploys",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "purged",
"queue_id": "QUEUE_ID",
"deleted": 2
}Claim the next item (leased for processing).
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/claim \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lease_seconds": 60}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/queue/deploys/claim \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lease_seconds": 60, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/claim",
headers={"x-api-key": API_KEY},
json={'lease_seconds': 60},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/claim",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'lease_seconds': 60, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/claim", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"lease_seconds": 60})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/claim", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"lease_seconds": 60, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/claim", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"lease_seconds": 60})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/claim", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"lease_seconds": 60, "platform": "my_garden"})
});{
"status": "claimed",
"queue_id": "QUEUE_ID",
"item": {
"queue_id": "QUEUE_ID",
"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
"user_id": "u123",
"priority": 100,
"payload": {
"any": "json"
},
"state": "claimed",
"created_at": "2026-07-08T14:02:11.517301+00:00",
"claimed_at": "2026-07-08T14:05:27.331904+00:00",
"lease_until": "2026-07-08T14:06:27.331904+00:00",
"claimed_by": "u_9f3a1c7b8e2d0a4f"
},
"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7"
}Enqueue: add an item to the queue.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/join \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id": "u123", "priority": 100, "payload": {"any": "json"}}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/queue/deploys/join \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id": "u123", "priority": 100, "payload": {"any": "json"}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/join",
headers={"x-api-key": API_KEY},
json={'user_id': 'u123', 'priority': 100, 'payload': {'any': 'json'}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/join",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'user_id': 'u123', 'priority': 100, 'payload': {'any': 'json'}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/join", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"user_id": "u123", "priority": 100, "payload": {"any": "json"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/join", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"user_id": "u123", "priority": 100, "payload": {"any": "json"}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/join", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"user_id": "u123", "priority": 100, "payload": {"any": "json"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/join", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"user_id": "u123", "priority": 100, "payload": {"any": "json"}, "platform": "my_garden"})
});{
"status": "enqueued",
"queue_id": "QUEUE_ID",
"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30"
}Peek at the next item without claiming it.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/next \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/queue/deploys/next?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/queue/deploys/next",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/queue/deploys/next",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/next", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/next?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/next", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/next?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"queue_id": "QUEUE_ID",
"item": {
"queue_id": "QUEUE_ID",
"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
"user_id": "u123",
"priority": 100,
"payload": {
"any": "json"
},
"state": "pending",
"created_at": "2026-07-08T14:02:11.517301+00:00",
"claimed_at": null,
"lease_until": null,
"claimed_by": null
}
}Renew the lease on a claimed item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/renew \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/queue/deploys/renew \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/renew",
headers={"x-api-key": API_KEY},
json={'item_id': '0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30', 'claim_token': '5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7', 'lease_seconds': 60},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/renew",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'item_id': '0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30', 'claim_token': '5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7', 'lease_seconds': 60, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/renew", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/renew", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/renew", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/renew", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", "claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "lease_seconds": 60, "platform": "my_garden"})
});{
"status": "renewed",
"queue_id": "QUEUE_ID",
"item": {
"queue_id": "QUEUE_ID",
"item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
"user_id": "u123",
"priority": 100,
"payload": {
"any": "json"
},
"state": "claimed",
"created_at": "2026-07-08T14:02:11.517301+00:00",
"claimed_at": "2026-07-08T14:05:27.331904+00:00",
"lease_until": "2026-07-08T14:07:12.908844+00:00",
"claimed_by": "u_9f3a1c7b8e2d0a4f"
}
}Delete a specific queued item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted",
"forced": false,
"queue_id": "QUEUE_ID",
"item_id": "ITEM_ID"
}Acknowledge (complete) a claimed item.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack",
headers={"x-api-key": API_KEY},
json={'claim_token': '5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'claim_token': '5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/queue/deploys/0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30/ack", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"claim_token": "5a1f0c8d-73b2-4e69-a4d5-6e0b9c2f18d7", "platform": "my_garden"})
});{
"status": "acknowledged",
"queue_id": "QUEUE_ID",
"item_id": "ITEM_ID"
}Streaks
Base URL https://api.bottlebluellc.com/v1
Read, update, or delete a user's streak. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug (streaks do not support "*"):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug (streaks do not support "*"):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"user_id": "USER_ID",
"current_streak": 5,
"best_streak": 12,
"last_active_date": "2026-07-08",
"version": 17,
"updated_at": "2026-07-08T14:05:27.331904"
}Read, update, or delete a user's streak.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
json={'current_streak': 5, 'best_streak': 12, 'last_active_date': '2026-07-07', 'version': 17},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'current_streak': 5, 'best_streak': 12, 'last_active_date': '2026-07-07', 'version': 17, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"current_streak": 5, "best_streak": 12, "last_active_date": "2026-07-07", "version": 17, "platform": "my_garden"})
});{
"status": "ok",
"user_id": "USER_ID",
"current_streak": 5,
"best_streak": 12,
"last_active_date": "2026-07-07",
"version": 18,
"updated_at": "2026-07-08T14:06:03.118220"
}Read, update, or delete a user's streak.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"best_streak": 12}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"best_streak": 12, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
json={'best_streak': 12},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'best_streak': 12, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"best_streak": 12})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"best_streak": 12, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"best_streak": 12})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"best_streak": 12, "platform": "my_garden"})
});{
"status": "ok",
"user_id": "USER_ID",
"current_streak": 5,
"best_streak": 12,
"last_active_date": "2026-07-07",
"version": 18,
"updated_at": "2026-07-08T14:06:03.118220"
}Read, update, or delete a user's streak.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "cleared",
"user_id": "USER_ID",
"deleted": 31
}Record a daily check-in, advancing the streak.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123/checkin \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"date": "2026-07-08"}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/streaks/u123/checkin \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"date": "2026-07-08", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/streaks/u123/checkin",
headers={"x-api-key": API_KEY},
json={'date': '2026-07-08'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/streaks/u123/checkin",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'date': '2026-07-08', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"date": "2026-07-08"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"date": "2026-07-08", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"date": "2026-07-08"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"date": "2026-07-08", "platform": "my_garden"})
});{
"status": "ok",
"user_id": "USER_ID",
"date": "2026-07-08",
"current_streak": 6,
"best_streak": 12,
"last_active_date": "2026-07-08",
"version": 18
}The user's streak history.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/streaks/u123/history \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/streaks/u123/history?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/streaks/u123/history",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/streaks/u123/history",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug (streaks do not support "*"):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/history?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug (streaks do not support "*"):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/streaks/u123/history?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"user_id": "USER_ID",
"start": "2026-06-09",
"end": "2026-07-08",
"days": [
"2026-07-03",
"2026-07-04",
"2026-07-05",
"2026-07-06",
"2026-07-07",
"2026-07-08"
]
}Leaderboards
Base URL https://api.bottlebluellc.com/v1
Submit a score to a leaderboard. On your own platform you may submit your own score; a Bottle Blue product-platform board is company-managed.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/leaderboard/score \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"score": 4200}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are company-managed
curl https://api.bottlebluellc.com/v1/leaderboard/score \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"score": 4200, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/leaderboard/score",
headers={"x-api-key": API_KEY},
json={'score': 4200},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/leaderboard/score",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are company-managed
json={'score': 4200, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/score", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"score": 4200})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/score", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are company-managed
body: JSON.stringify({"score": 4200, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/score", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"score": 4200})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/score", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are company-managed
body: JSON.stringify({"score": 4200, "platform": "my_garden"})
});{
"message": "Score submitted",
"user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"score": 4200
}Top scores on a leaderboard. On a Bottle Blue product board this is the shared, public ranking; on your own project platform the board is private, so it returns only your own entry.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/leaderboard/top \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/leaderboard/top?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/leaderboard/top",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/leaderboard/top",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/top", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/top?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/top", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/top?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"rank": 1,
"user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"score": 4200,
"updated_at": "2026-07-08T14:07:45.220145"
},
{
"rank": 2,
"user_id": "u_2b3c4d5e6f7a8b9c", "isYou": false,
"score": 3100,
"updated_at": "2026-07-07T21:33:02.994310"
},
{
"rank": 3,
"user_id": "u_3c4d5e6f7a8b9c0d", "isYou": false,
"score": 2875,
"updated_at": "2026-07-06T18:12:59.410877"
}
]Read or remove a user's leaderboard entry. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/leaderboard/you@example.com \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/leaderboard/you@example.com",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/leaderboard/you@example.com",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"score": 4200,
"updated_at": "2026-07-08T14:07:45.220145"
}Read or remove a user's leaderboard entry.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/leaderboard/you@example.com \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are company-managed
curl "https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/leaderboard/you@example.com",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/leaderboard/you@example.com",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are company-managed
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are company-managed
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are company-managed
const scoped = await fetch("https://api.bottlebluellc.com/v1/leaderboard/you@example.com?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Score deleted for you@example.com"
}Scoreboards
Base URL https://api.bottlebluellc.com/v1
NOT_MATCH_OWNER on a write.Read or delete a live scoreboard for a match. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/scoreboard/match-001 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/scoreboard/match-001",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/scoreboard/match-001",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"match_id": "MATCH_ID",
"teams": [
{
"team_id": "TEAM_ID",
"score": 21,
"updated_at": "2026-07-08T14:09:12.554873"
},
{
"team_id": "team-blue",
"score": 14,
"updated_at": "2026-07-08T14:08:41.220981"
}
]
}Read or delete a live scoreboard for a match.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/scoreboard/match-001 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/scoreboard/match-001",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/scoreboard/match-001",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "cleared",
"match_id": "MATCH_ID",
"deleted": 2
}Set or update a team's score.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"score": 21}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"score": 21, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red",
headers={"x-api-key": API_KEY},
json={'score': 21},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'score': 21, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"score": 21})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"score": 21, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"score": 21})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"score": 21, "platform": "my_garden"})
});{
"status": "ok",
"mode": "set",
"match_id": "MATCH_ID",
"team_id": "TEAM_ID",
"score": 21
}Set or update a team's score.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"delta": 3}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"delta": 3, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red",
headers={"x-api-key": API_KEY},
json={'delta': 3},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'delta': 3, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"delta": 3})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"delta": 3, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"delta": 3})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"delta": 3, "platform": "my_garden"})
});{
"status": "ok",
"mode": "delta",
"match_id": "MATCH_ID",
"team_id": "TEAM_ID",
"score": 24
}Increment a team's score.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"delta": 1}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"delta": 1, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment",
headers={"x-api-key": API_KEY},
json={'delta': 1},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'delta': 1, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"delta": 1})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"delta": 1, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"delta": 1})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/scoreboard/match-001/team/team-red/increment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"delta": 1, "platform": "my_garden"})
});{
"status": "ok",
"match_id": "MATCH_ID",
"team_id": "TEAM_ID",
"score": 25
}Assets
Base URL https://api.bottlebluellc.com/v1
List a user's assets.
Each asset carries its reservation fields (reservedFor, reservedBy, reservedAt, reservedFrom, reservedUntil) only while a hold is in place.
| Parameter | In | Description |
|---|---|---|
orgId | query | Optional. Scope to a shared organization's asset instead of your own account - see Organizations. Any member role can list. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/assets \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/assets?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/assets",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/assets",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform) or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"assets": [
{
"entityType": "asset",
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"name": "Garden shed",
"type": "structure",
"assetType": "structure",
"status": "available",
"platform": "default",
"createdAt": "2026-07-08T09:30:12Z",
"createdBy": "owner@acme.com",
"metadata": {
"color": "green"
}
}
]
}Create an asset.
| Parameter | In | Description |
|---|---|---|
orgId | body | Optional. Scope to a shared organization's asset instead of your own account - see Organizations. Any member role can create. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/assets \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/assets \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets",
headers={"x-api-key": API_KEY},
json={'name': 'Garden shed', 'type': 'structure', 'metadata': {'color': 'green'}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'name': 'Garden shed', 'type': 'structure', 'metadata': {'color': 'green'}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"name": "Garden shed", "type": "structure", "metadata": {"color": "green"}, "platform": "my_garden"})
});{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07"
}Get a single asset. Supports ?expand=links to inline this record's links in the same call - see Link System.
The reservation fields reservedFor, reservedBy, reservedAt, reservedFrom and reservedUntil are present only while a hold is in place.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
orgId | query | Optional. Scope to a shared organization's asset instead of your own account - see Organizations. Any member role can read. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name or a Bottle Blue product slug:
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name or a Bottle Blue product slug:
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"entityType": "asset",
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"name": "Garden shed",
"type": "structure",
"assetType": "structure",
"status": "available",
"platform": "default",
"createdAt": "2026-07-08T09:30:12Z",
"createdBy": "owner@acme.com",
"metadata": {
"color": "green"
},
"reservedFor": "Sam Rivera",
"reservedBy": "owner@acme.com",
"reservedAt": "2026-09-05T10:42:00Z",
"reservedFrom": "2026-09-08T08:00:00Z",
"reservedUntil": "2026-09-09T17:00:00Z"
}Update an asset's name, category, subtype, status, or metadata.
Every body field is optional, but at least one must be present: a request that would change nothing returns 400 {"error": "No fields to update"}, so a mistyped field name cannot look like a success. metadata is merged into the stored object rather than replacing it, so keys you leave out keep their current values, and omitting metadata entirely leaves it alone.
checked_out is not settable here and returns 400. That status is reachable only through check-out, which is what keeps it from ever disagreeing with checkedOutTo, checkedOutBy and checkedOutAt. Changing status while the asset is checked out returns 409 CHECKED_OUT; every other field can still be patched while it is out.
A status change appends a status_change event to the asset's history. A rename or a metadata edit does not: the history is a custody log, not a field-level audit trail.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
name | body | Optional. New display name. Non-empty string; surrounding whitespace is trimmed. |
type | body | Optional. The asset's category: tools, vehicles, electronics, components or other. Non-empty string. |
assetType | body | Optional. Free-text subtype, for detail the fixed category list does not carry (for example cordless drill). Non-empty string. |
status | body | Optional. One of available, maintenance or lost. checked_out is rejected here. |
metadata | body | Optional. An object of your own fields, merged into the existing metadata rather than replacing it. |
orgId | body | Optional. Scope to a shared organization's asset instead of your own account - see Organizations. Any member role can edit. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Garden shed", "status": "maintenance", "metadata": {"notes": "Repainted this spring"}}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"metadata": {"notes": "Repainted this spring"}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
json={'name': 'Garden shed', 'status': 'maintenance', 'metadata': {'notes': 'Repainted this spring'}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
json={'metadata': {'notes': 'Repainted this spring'}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Garden shed", "status": "maintenance", "metadata": {"notes": "Repainted this spring"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"metadata": {"notes": "Repainted this spring"}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Garden shed", "status": "maintenance", "metadata": {"notes": "Repainted this spring"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
body: JSON.stringify({"metadata": {"notes": "Repainted this spring"}, "platform": "my_garden"})
});{
"message": "Asset updated"
}{
"error": "No fields to update"
}{
"error": "Check the asset in before changing its status",
"code": "CHECKED_OUT"
}Delete an asset.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
orgId | body | Optional. Scope to a shared organization's asset instead of your own account - see Organizations. Requires the admin role or higher (deleting inventory outright is more destructive than checking it in or out). |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
// "bb_aquabotany" Aqua Botany, "bb_pocketquant" PocketQuant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Asset deleted"
}Check an asset out to someone. Sets status to checked_out and appends an event to the asset's history.
If the asset is reserved for someone whose name does not match checkedOutTo, the call returns 409 RESERVED_FOR_OTHER unless you pass override. The two names are compared with surrounding whitespace stripped and letter case ignored, since both sides are free text a person typed. A successful check-out consumes any reservation, clearing reservedFor, reservedBy, reservedAt, reservedFrom and reservedUntil.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
checkedOutTo | body | required Free-text name of the person or place the asset is going to. No login is required for the recipient. |
dueBackAt | body | Optional ISO-8601 timestamp. |
notes | body | Optional free text, stored on the history event. |
override | body | Optional boolean. Check the asset out even though it is reserved for someone else. |
orgId | body | Optional. Scope to a shared organization's asset instead of your own - see Organizations. Any member role can check out. |
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkout \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"checkedOutTo": "Jane Doe", "dueBackAt": "2026-09-06T00:00:00Z", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkout",
headers={"x-api-key": API_KEY},
json={
'checkedOutTo': 'Jane Doe',
'dueBackAt': '2026-09-06T00:00:00Z',
'orgId': '3f1c9e2a7d614b839e5a3f8b1c6d2e07', # omit for a personal asset
},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkout", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
checkedOutTo: "Jane Doe",
dueBackAt: "2026-09-06T00:00:00Z",
orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" // omit for a personal asset
})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkout", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
checkedOutTo: "Jane Doe",
dueBackAt: "2026-09-06T00:00:00Z",
orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" // omit for a personal asset
})
});
const data = await resp.json();{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"status": "checked_out",
"checkedOutTo": "Jane Doe",
"checkedOutBy": "owner@acme.com",
"checkedOutAt": "2026-08-30T12:15:00Z",
"dueBackAt": "2026-09-06T00:00:00Z"
}{
"error": "Asset is already checked out",
"code": "ALREADY_CHECKED_OUT"
}{
"error": "Asset is reserved for Sam Rivera; pass override to check it out anyway",
"code": "RESERVED_FOR_OTHER"
}Check an asset back in. Sets status back to available, clears the checkout fields, and appends an event to the asset's history.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
notes | body | Optional free text, stored on the history event. |
orgId | body | Optional. Same organization scoping as check-out. |
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkin \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notes": "returned in good condition", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkin",
headers={"x-api-key": API_KEY},
json={'notes': 'returned in good condition', 'orgId': '3f1c9e2a7d614b839e5a3f8b1c6d2e07'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ notes: "returned in good condition", orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/checkin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ notes: "returned in good condition", orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" })
});
const data = await resp.json();{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"status": "available",
"checkedInBy": "owner@acme.com",
"checkedInAt": "2026-09-02T09:00:00Z"
}{
"error": "Asset is not checked out",
"code": "NOT_CHECKED_OUT"
}Hold an asset for a named person.
A reservation is a separate axis from status, not another status value. The common case is holding something that is currently checked out to someone else, which a single status field cannot express, so reserving never changes status and never blocks a check-in.
Returns 400 if reservedFor is missing, empty, or longer than 200 characters, 404 if there is no such asset, and 409 ALREADY_RESERVED if the asset is already held. Release a hold with POST /assets/{id}/unreserve; a successful check-out consumes it instead. The reservation fields appear on GET /assets/{id} and GET /assets while the hold is in place, and the hold is recorded as a reserve event in the asset's history.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
reservedFor | body | required Free-text name of the person the asset is being held for. Maximum 200 characters. No login is required for that person. |
reservedFrom | body | Optional ISO-8601 timestamp for when the hold starts. |
reservedUntil | body | Optional ISO-8601 timestamp for when the hold ends. |
notes | body | Optional free text, stored on the history event. |
orgId | body | Optional. Scope to a shared organization's asset instead of your own account (see Organizations). Any member role can reserve. |
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/reserve \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reservedFor": "Sam Rivera", "reservedFrom": "2026-09-08T08:00:00Z", "reservedUntil": "2026-09-09T17:00:00Z", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/reserve",
headers={"x-api-key": API_KEY},
json={
'reservedFor': 'Sam Rivera',
'reservedFrom': '2026-09-08T08:00:00Z',
'reservedUntil': '2026-09-09T17:00:00Z',
'orgId': '3f1c9e2a7d614b839e5a3f8b1c6d2e07', # omit for a personal asset
},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/reserve", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
reservedFor: "Sam Rivera",
reservedFrom: "2026-09-08T08:00:00Z",
reservedUntil: "2026-09-09T17:00:00Z",
orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" // omit for a personal asset
})
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/reserve", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
reservedFor: "Sam Rivera",
reservedFrom: "2026-09-08T08:00:00Z",
reservedUntil: "2026-09-09T17:00:00Z",
orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" // omit for a personal asset
})
});
const data = await resp.json();{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"reservedFor": "Sam Rivera",
"reservedBy": "owner@acme.com",
"reservedAt": "2026-09-05T10:42:00Z",
"reservedFrom": "2026-09-08T08:00:00Z",
"reservedUntil": "2026-09-09T17:00:00Z"
}{
"error": "Asset is already reserved",
"code": "ALREADY_RESERVED"
}Release an asset's reservation.
Any member may release a hold, matching check-in: a reservation is a shared signal about who is next in line, not a private lock. Returns 404 if there is no such asset and 409 NOT_RESERVED if nothing is holding it. Clearing a hold appends an unreserve event to the asset's history.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
notes | body | Optional free text, stored on the history event. |
orgId | body | Optional. Same organization scoping as reserve. |
curl https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/unreserve \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notes": "install rescheduled", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/unreserve",
headers={"x-api-key": API_KEY},
json={'notes': 'install rescheduled', 'orgId': '3f1c9e2a7d614b839e5a3f8b1c6d2e07'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/unreserve", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ notes: "install rescheduled", orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/unreserve", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ notes: "install rescheduled", orgId: "3f1c9e2a7d614b839e5a3f8b1c6d2e07" })
});
const data = await resp.json();{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"reservationReleasedBy": "owner@acme.com"
}{
"error": "Asset is not reserved",
"code": "NOT_RESERVED"
}The asset's full custody history, newest first.
Every event records who did it and when. action is one of checkout, checkin, reserve, unreserve or status_change; a status_change event also carries fromStatus and toStatus. source is "manual" on every event today and exists as a hook for future automated check-in/out: a camera-driven check-in (for example, a Roost camera) will show up here with "source": "roost_camera" instead, with no other change to this endpoint.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
orgId | query | Optional. Same organization scoping as check-out. |
curl "https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/history?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/history",
headers={"x-api-key": API_KEY},
params={"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/history?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07/history?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
"events": [
{ "action": "status_change", "by": "owner@acme.com", "at": "2026-09-03T11:20:00Z", "fromStatus": "available", "toStatus": "maintenance", "notes": null, "source": "manual" },
{ "action": "unreserve", "by": "owner@acme.com", "at": "2026-09-02T10:05:00Z", "to": "Sam Rivera", "notes": "install rescheduled", "source": "manual" },
{ "action": "checkin", "by": "owner@acme.com", "at": "2026-09-02T09:00:00Z", "to": null, "notes": "returned in good condition", "source": "manual" },
{ "action": "reserve", "by": "owner@acme.com", "at": "2026-08-31T08:40:00Z", "to": "Sam Rivera", "notes": null, "source": "manual" },
{ "action": "checkout", "by": "owner@acme.com", "at": "2026-08-30T12:15:00Z", "to": "Jane Doe", "notes": null, "source": "manual" }
]
}Calculators
Base URL https://api.bottlebluellc.com/v1
Discovery: catalog of every finance / fitness / real-estate calculator + its parameters.
curl "https://api.bottlebluellc.com/v1/calculate" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/calculate",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/calculate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/calculate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domains": {
"finance": { "mortgage": { "description": "Monthly mortgage payment + total interest", "params": ["principal", "annual_rate_pct", "years"] }, "compound-interest": { "...": "..." } },
"fitness": { "bmr": { "...": "..." } },
"realestate": { "cap-rate": { "...": "..." } }
},
"enums": { "activity": ["active", "light", "moderate", "sedentary", "very_active"], "sex": ["female", "male"] }
}Finance calculators (pure-compute): mortgage, compound-interest, loan-payment, roi, savings-goal, inflation-adjust. Use /finance/calculate/catalog to list all.
curl "https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "mortgage",
"inputs": {
"principal": "300000",
"annual_rate_pct": "6.5",
"years": "30"
},
"result": {
"monthly_payment": 1896.2,
"number_of_payments": 360,
"total_paid": 682633.47,
"total_interest": 382633.47
}
}All six finance calculators, each as a full request/response example:
mortgageMonthly mortgage payment plus total interest over the loan.principal, annual_rate_pct, years
Monthly mortgage payment plus total interest over the loan.
curl "https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "mortgage",
"inputs": {
"principal": "300000",
"annual_rate_pct": "6.5",
"years": "30"
},
"result": {
"monthly_payment": 1896.2,
"number_of_payments": 360,
"total_paid": 682633.47,
"total_interest": 382633.47
}
}compound-interestFuture value with optional monthly contributions.principal, annual_rate_pct, years, compounds_per_year?, monthly_contribution?
Future value with optional monthly contributions.
curl "https://api.bottlebluellc.com/v1/finance/calculate/compound-interest?principal=10000&annual_rate_pct=7&years=20&compounds_per_year=12&monthly_contribution=200" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/compound-interest?principal=10000&annual_rate_pct=7&years=20&compounds_per_year=12&monthly_contribution=200",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/compound-interest?principal=10000&annual_rate_pct=7&years=20&compounds_per_year=12&monthly_contribution=200", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/compound-interest?principal=10000&annual_rate_pct=7&years=20&compounds_per_year=12&monthly_contribution=200", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "compound-interest",
"inputs": {
"principal": "10000",
"annual_rate_pct": "7",
"years": "20",
"compounds_per_year": "12",
"monthly_contribution": "200"
},
"result": {
"future_value": 144572.72,
"total_contributions": 58000.0,
"total_interest": 86572.72
}
}loan-paymentAmortizing loan payment (same math as mortgage, generic wording).principal, annual_rate_pct, years
Amortizing loan payment (same math as mortgage, generic wording).
curl "https://api.bottlebluellc.com/v1/finance/calculate/loan-payment?principal=35000&annual_rate_pct=8&years=5" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/loan-payment?principal=35000&annual_rate_pct=8&years=5",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/loan-payment?principal=35000&annual_rate_pct=8&years=5", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/loan-payment?principal=35000&annual_rate_pct=8&years=5", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "loan-payment",
"inputs": {
"principal": "35000",
"annual_rate_pct": "8",
"years": "5"
},
"result": {
"monthly_payment": 709.67,
"number_of_payments": 60,
"total_paid": 42580.43,
"total_interest": 7580.43
}
}roiReturn on investment, plus annualized CAGR when years is given.initial_value, final_value, years?
Return on investment, plus annualized CAGR when years is given.
curl "https://api.bottlebluellc.com/v1/finance/calculate/roi?initial_value=15000&final_value=24000&years=4" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/roi?initial_value=15000&final_value=24000&years=4",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/roi?initial_value=15000&final_value=24000&years=4", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/roi?initial_value=15000&final_value=24000&years=4", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "roi",
"inputs": {
"initial_value": "15000",
"final_value": "24000",
"years": "4"
},
"result": {
"roi_pct": 60.0,
"net_gain": 9000.0,
"annualized_return_pct": 12.47
}
}savings-goalMonthly contribution required to reach a savings goal.goal_amount, years, annual_rate_pct?, current_savings?
Monthly contribution required to reach a savings goal.
curl "https://api.bottlebluellc.com/v1/finance/calculate/savings-goal?goal_amount=20000&years=3&annual_rate_pct=4¤t_savings=2000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/savings-goal?goal_amount=20000&years=3&annual_rate_pct=4¤t_savings=2000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/savings-goal?goal_amount=20000&years=3&annual_rate_pct=4¤t_savings=2000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/savings-goal?goal_amount=20000&years=3&annual_rate_pct=4¤t_savings=2000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "savings-goal",
"inputs": {
"goal_amount": "20000",
"years": "3",
"annual_rate_pct": "4",
"current_savings": "2000"
},
"result": {
"monthly_contribution_required": 464.77,
"months": 36,
"projected_from_current_savings": 2254.54
}
}inflation-adjustFuture value needed to match today's buying power, and today's buying power of a future amount.amount, annual_inflation_pct, years
Future value needed to match today's buying power, and today's buying power of a future amount.
curl "https://api.bottlebluellc.com/v1/finance/calculate/inflation-adjust?amount=10000&annual_inflation_pct=3.2&years=10" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/inflation-adjust?amount=10000&annual_inflation_pct=3.2&years=10",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/inflation-adjust?amount=10000&annual_inflation_pct=3.2&years=10", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/inflation-adjust?amount=10000&annual_inflation_pct=3.2&years=10", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"type": "inflation-adjust",
"inputs": {
"amount": "10000",
"annual_inflation_pct": "3.2",
"years": "10"
},
"result": {
"future_value_needed_to_match": 13702.41,
"todays_buying_power_of_amount_in_future": 7297.99
}
}Per-domain catalog: every finance calculator with its description and parameters.
The same pattern works for the other domains: /fitness/calculate/catalog and /realestate/calculate/catalog. Parameters suffixed ? are optional. For the cross-domain catalog plus parameter enums, use /calculate.
curl "https://api.bottlebluellc.com/v1/finance/calculate/catalog" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/finance/calculate/catalog",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/catalog", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/finance/calculate/catalog", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "finance",
"calculators": {
"mortgage": {
"description": "Monthly mortgage payment + total interest",
"params": ["principal", "annual_rate_pct", "years"]
},
"compound-interest": {
"description": "Future value with optional monthly contributions",
"params": ["principal", "annual_rate_pct", "years", "compounds_per_year?", "monthly_contribution?"]
},
"loan-payment": {
"description": "Amortizing loan payment",
"params": ["principal", "annual_rate_pct", "years"]
},
"roi": {
"description": "Return on investment (+ annualized CAGR if years given)",
"params": ["initial_value", "final_value", "years?"]
},
"savings-goal": {
"description": "Monthly contribution to reach a savings goal",
"params": ["goal_amount", "years", "annual_rate_pct?", "current_savings?"]
},
"inflation-adjust": {
"description": "Inflation-adjusted value over time",
"params": ["amount", "annual_inflation_pct", "years"]
}
}
}Fitness calculators (pure-compute): bmi, bmr, tdee, body-fat, macros, one-rep-max, calories-burned, ideal-weight. Use /fitness/calculate/catalog to list all.
curl "https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "tdee",
"inputs": {
"weight_kg": "80",
"height_cm": "180",
"age": "30",
"sex": "male",
"activity": "moderate"
},
"result": {
"bmr_kcal_per_day": 1780.0,
"tdee_kcal_per_day": 2759.0,
"activity_multiplier": 1.55
}
}All eight fitness calculators, each as a full request/response example:
bmiBody mass index and weight category.weight_kg, height_cm
Body mass index and weight category.
curl "https://api.bottlebluellc.com/v1/fitness/calculate/bmi?weight_kg=80&height_cm=180" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/bmi?weight_kg=80&height_cm=180",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/bmi?weight_kg=80&height_cm=180", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/bmi?weight_kg=80&height_cm=180", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "bmi",
"inputs": {
"weight_kg": "80",
"height_cm": "180"
},
"result": {
"bmi": 24.7,
"category": "normal"
}
}bmrBasal metabolic rate (Mifflin-St Jeor).weight_kg, height_cm, age, sex
Basal metabolic rate (Mifflin-St Jeor).
curl "https://api.bottlebluellc.com/v1/fitness/calculate/bmr?weight_kg=80&height_cm=180&age=30&sex=male" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/bmr?weight_kg=80&height_cm=180&age=30&sex=male",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/bmr?weight_kg=80&height_cm=180&age=30&sex=male", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/bmr?weight_kg=80&height_cm=180&age=30&sex=male", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "bmr",
"inputs": {
"weight_kg": "80",
"height_cm": "180",
"age": "30",
"sex": "male"
},
"result": {
"bmr_kcal_per_day": 1780.0
}
}tdeeTotal daily energy expenditure.weight_kg, height_cm, age, sex, activity
Total daily energy expenditure.
curl "https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "tdee",
"inputs": {
"weight_kg": "80",
"height_cm": "180",
"age": "30",
"sex": "male",
"activity": "moderate"
},
"result": {
"bmr_kcal_per_day": 1780.0,
"tdee_kcal_per_day": 2759.0,
"activity_multiplier": 1.55
}
}body-fatBody-fat percentage, US Navy circumference method.sex, height_cm, neck_cm, waist_cm, hip_cm (female only)
Body-fat percentage, US Navy circumference method.
curl "https://api.bottlebluellc.com/v1/fitness/calculate/body-fat?sex=male&height_cm=180&neck_cm=38&waist_cm=86" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/body-fat?sex=male&height_cm=180&neck_cm=38&waist_cm=86",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/body-fat?sex=male&height_cm=180&neck_cm=38&waist_cm=86", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/body-fat?sex=male&height_cm=180&neck_cm=38&waist_cm=86", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "body-fat",
"inputs": {
"sex": "male",
"height_cm": "180",
"neck_cm": "38",
"waist_cm": "86"
},
"result": {
"body_fat_pct": 16.9,
"method": "US Navy circumference"
}
}macrosDaily calorie and macro targets for a goal.weight_kg, height_cm, age, sex, activity, goal?
Daily calorie and macro targets for a goal.
curl "https://api.bottlebluellc.com/v1/fitness/calculate/macros?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate&goal=cut" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/macros?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate&goal=cut",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/macros?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate&goal=cut", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/macros?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate&goal=cut", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "macros",
"inputs": {
"weight_kg": "80",
"height_cm": "180",
"age": "30",
"sex": "male",
"activity": "moderate",
"goal": "cut"
},
"result": {
"target_calories": 2207.0,
"protein_g": 160.0,
"fat_g": 61.0,
"carbs_g": 254.0,
"goal": "cut"
}
}one-rep-maxEstimated one-rep max (Epley and Brzycki).weight, reps
Estimated one-rep max (Epley and Brzycki).
curl "https://api.bottlebluellc.com/v1/fitness/calculate/one-rep-max?weight=185&reps=5" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/one-rep-max?weight=185&reps=5",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/one-rep-max?weight=185&reps=5", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/one-rep-max?weight=185&reps=5", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "one-rep-max",
"inputs": {
"weight": "185",
"reps": "5"
},
"result": {
"one_rep_max_epley": 215.83,
"one_rep_max_brzycki": 208.12,
"estimate": 211.98
}
}calories-burnedCalories burned for an activity (MET-based).weight_kg, minutes, activity
Calories burned for an activity (MET-based).
curl "https://api.bottlebluellc.com/v1/fitness/calculate/calories-burned?weight_kg=80&minutes=45&activity=running" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/calories-burned?weight_kg=80&minutes=45&activity=running",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/calories-burned?weight_kg=80&minutes=45&activity=running", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/calories-burned?weight_kg=80&minutes=45&activity=running", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "calories-burned",
"inputs": {
"weight_kg": "80",
"minutes": "45",
"activity": "running"
},
"result": {
"calories_burned": 588.0,
"met": 9.8,
"activity": "running"
}
}ideal-weightHealthy weight range for a height (BMI 18.5 to 24.9).height_cm
Healthy weight range for a height (BMI 18.5 to 24.9).
curl "https://api.bottlebluellc.com/v1/fitness/calculate/ideal-weight?height_cm=180" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/fitness/calculate/ideal-weight?height_cm=180",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/ideal-weight?height_cm=180", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/fitness/calculate/ideal-weight?height_cm=180", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "fitness",
"type": "ideal-weight",
"inputs": {
"height_cm": "180"
},
"result": {
"healthy_weight_kg_min": 59.9,
"healthy_weight_kg_max": 80.7,
"basis": "BMI 18.5-24.9"
}
}Real-estate calculators (pure-compute): cap-rate, cash-on-cash, noi, gross-rent-multiplier, dscr, affordability, price-per-sqft, rent-vs-buy. Use /realestate/calculate/catalog to list all.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "cap-rate",
"inputs": {
"property_value": "400000",
"noi": "24000"
},
"result": { "cap_rate_pct": 6.0, "noi": 24000.0 }
}All eight real-estate calculators, each as a full request/response example:
cap-rateCapitalization rate.property_value, noi (or gross_income + operating_expenses)
Capitalization rate.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cap-rate?property_value=400000&noi=24000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "cap-rate",
"inputs": {
"property_value": "400000",
"noi": "24000"
},
"result": {
"cap_rate_pct": 6.0,
"noi": 24000.0
}
}cash-on-cashCash-on-cash return.annual_cash_flow, cash_invested
Cash-on-cash return.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/cash-on-cash?annual_cash_flow=9600&cash_invested=80000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/cash-on-cash?annual_cash_flow=9600&cash_invested=80000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cash-on-cash?annual_cash_flow=9600&cash_invested=80000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/cash-on-cash?annual_cash_flow=9600&cash_invested=80000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "cash-on-cash",
"inputs": {
"annual_cash_flow": "9600",
"cash_invested": "80000"
},
"result": {
"cash_on_cash_pct": 12.0
}
}noiNet operating income and expense ratio.gross_income, operating_expenses
Net operating income and expense ratio.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/noi?gross_income=60000&operating_expenses=22000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/noi?gross_income=60000&operating_expenses=22000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/noi?gross_income=60000&operating_expenses=22000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/noi?gross_income=60000&operating_expenses=22000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "noi",
"inputs": {
"gross_income": "60000",
"operating_expenses": "22000"
},
"result": {
"noi": 38000.0,
"expense_ratio_pct": 36.67
}
}gross-rent-multiplierGross rent multiplier.property_price, annual_gross_rent
Gross rent multiplier.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/gross-rent-multiplier?property_price=350000&annual_gross_rent=30000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/gross-rent-multiplier?property_price=350000&annual_gross_rent=30000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/gross-rent-multiplier?property_price=350000&annual_gross_rent=30000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/gross-rent-multiplier?property_price=350000&annual_gross_rent=30000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "gross-rent-multiplier",
"inputs": {
"property_price": "350000",
"annual_gross_rent": "30000"
},
"result": {
"gross_rent_multiplier": 11.67
}
}dscrDebt service coverage ratio.annual_debt_service, noi (or gross_income + operating_expenses)
Debt service coverage ratio.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/dscr?gross_income=60000&operating_expenses=22000&annual_debt_service=28000" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/dscr?gross_income=60000&operating_expenses=22000&annual_debt_service=28000",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/dscr?gross_income=60000&operating_expenses=22000&annual_debt_service=28000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/dscr?gross_income=60000&operating_expenses=22000&annual_debt_service=28000", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "dscr",
"inputs": {
"gross_income": "60000",
"operating_expenses": "22000",
"annual_debt_service": "28000"
},
"result": {
"dscr": 1.36,
"lender_view": "pass (>=1.25)"
}
}affordabilityMax affordable home price using the 28/36 rule.annual_income, annual_rate_pct, monthly_debt?, down_payment?, years?
Max affordable home price using the 28/36 rule.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/affordability?annual_income=95000&annual_rate_pct=6.5&monthly_debt=300&down_payment=40000&years=30" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/affordability?annual_income=95000&annual_rate_pct=6.5&monthly_debt=300&down_payment=40000&years=30",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/affordability?annual_income=95000&annual_rate_pct=6.5&monthly_debt=300&down_payment=40000&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/affordability?annual_income=95000&annual_rate_pct=6.5&monthly_debt=300&down_payment=40000&years=30", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "affordability",
"inputs": {
"annual_income": "95000",
"annual_rate_pct": "6.5",
"monthly_debt": "300",
"down_payment": "40000",
"years": "30"
},
"result": {
"max_monthly_housing_payment": 2216.67,
"max_loan_amount": 350700.65,
"max_home_price": 390700.65,
"rule": "28/36 front/back-end ratios"
}
}price-per-sqftPrice per square foot.price, square_feet
Price per square foot.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/price-per-sqft?price=425000&square_feet=1850" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/price-per-sqft?price=425000&square_feet=1850",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/price-per-sqft?price=425000&square_feet=1850", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/price-per-sqft?price=425000&square_feet=1850", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "price-per-sqft",
"inputs": {
"price": "425000",
"square_feet": "1850"
},
"result": {
"price_per_sqft": 229.73
}
}rent-vs-buyMonthly cost comparison of owning versus renting.home_price, annual_rate_pct, monthly_rent, down_payment?, years?
Monthly cost comparison of owning versus renting.
curl "https://api.bottlebluellc.com/v1/realestate/calculate/rent-vs-buy?home_price=400000&down_payment=40000&annual_rate_pct=6.5&years=30&monthly_rent=2200" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/realestate/calculate/rent-vs-buy?home_price=400000&down_payment=40000&annual_rate_pct=6.5&years=30&monthly_rent=2200",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/rent-vs-buy?home_price=400000&down_payment=40000&annual_rate_pct=6.5&years=30&monthly_rent=2200", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/realestate/calculate/rent-vs-buy?home_price=400000&down_payment=40000&annual_rate_pct=6.5&years=30&monthly_rent=2200", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"domain": "realestate",
"type": "rent-vs-buy",
"inputs": {
"home_price": "400000",
"down_payment": "40000",
"annual_rate_pct": "6.5",
"years": "30",
"monthly_rent": "2200"
},
"result": {
"estimated_monthly_cost_to_own": 2775.44,
"monthly_rent": 2200.0,
"monthly_difference_own_minus_rent": 575.44,
"note": "owning cost approximates mortgage + 1.5%/yr of price for tax/insurance/maintenance; excludes equity build & appreciation"
}
}Market analysis
Base URL https://api.bottlebluellc.com/v1
One-call macro dashboard: latest interest rates, economic indicators, national debt, and market sentiment.
curl "https://api.bottlebluellc.com/v1/market/econ-summary" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/econ-summary",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/econ-summary", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/econ-summary", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"summary": {
"rates": { "as_of": "2026-07-11", "count": 20, "items": [ { "id": "DFF", "label": "Federal Funds Rate", "value": "4.33", "unit": "%" } ] },
"economy": { "as_of": "2026-07-11", "items": [ { "id": "CPIAUCSL", "label": "CPI (All Items)", "value": "333.979", "unit": "index" } ] },
"fiscal": { "items": [ { "id": "TOTAL", "label": "US Total Public Debt Outstanding" } ] },
"sentiment": { "items": [ { "id": "CRYPTO_FEAR_GREED", "value": 26, "classification": "Fear" } ] }
},
"generated_at": "2026-07-11T20:00:00Z"
}Technical indicators (SMA/EMA/RSI/MACD/Bollinger/ROC + 52-week range + signals) for a ticker. Tracked instruments are everything the daily pipeline stores a close price for: every instrument in the indices, global-indices, sectors, crypto, commodities, fx, and bonds market categories (see /market/{category} for the current instrument list in each). Those resolve against our own history; any other symbol resolves through free upstreams.
curl "https://api.bottlebluellc.com/v1/market/technicals/^GSPC" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/market/technicals/^GSPC",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/market/technicals/^GSPC", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/market/technicals/^GSPC", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"ticker": "^GSPC",
"points_used": 273,
"indicators": {
"latest_close": 7575.39, "sma_20": 7480.1, "sma_50": 7301.5, "sma_200": 6650.2,
"rsi_14": 60.02, "macd": 61.2, "macd_signal": 55.4, "macd_histogram": 5.8,
"bollinger_upper": 7702.0, "bollinger_lower": 7258.2, "high_52w": 7620.0, "low_52w": 5100.0
},
"signals": { "trend": "bullish", "rsi_zone": "neutral", "macd": "bullish" }
}Organizations (shared, multi-user access)
An organization is a shared team: several different Bottle Blue accounts can jointly own the same data, with one of three roles - owner, admin, or member. Every other endpoint in this API (including a plain /assets call with no orgId) stays private to one account; organizations are the one way to share data across accounts. Any building block that supports shared ownership accepts an optional orgId to opt into it - today that is Assets.
How this differs from Roles & permissions (RBAC)
Roles & permissions is a separate, operator-only tool Bottle Blue LLC uses internally: gated by the x-admin-key header, with one flat role and member list per entire product platform. Organizations are the customer-facing opposite - any authenticated account can create one and invite other accounts into it with a normal API key or signed-in session, and every organization is its own isolated team. Two different organizations never see each other's members or data.
Roles
- owner - full control, including deleting the organization and managing any member, including other owners. Every organization always has at least one owner; removing or demoting the last owner is rejected with
409 LAST_OWNER. - admin - can invite and remove members and change roles, but cannot touch an owner and cannot delete the organization.
- member - can read and write the organization's shared data (for example, checking an asset in or out), but cannot manage membership.
The platform field on an organization
An organization's platform works exactly like every other building block's: omit it for the default namespace, send your own project name, or send a registered Bottle Blue product slug such as bb_trackless. The bb_ prefix is reserved - you can never claim a bb_ name for your own project, so a project can never collide with a current or future Bottle Blue product. See Data platforms for the full rule set.
Deleting an organization that still holds data (for example, assets created under it) is refused by default with 409 ORG_HAS_DATA. Pass ?force=true to delete the organization AND everything stored under it in one call.
Organizations
Base URL https://api.bottlebluellc.com/v1
Create an organization. The caller becomes its owner.
curl https://api.bottlebluellc.com/v1/organizations \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "platform": "bb_trackless"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/organizations",
headers={"x-api-key": API_KEY},
json={'name': 'Acme Corp', 'platform': 'bb_trackless'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Acme Corp", platform: "bb_trackless" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Acme Corp", platform: "bb_trackless" })
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"name": "Acme Corp",
"role": "owner",
"platform": "bb_trackless",
"createdAt": "2026-08-30T12:00:00Z"
}List the organizations you belong to.
| Parameter | In | Description |
|---|---|---|
platform | query | Optional. Scope to one platform, or * for every platform you belong to an organization in. Omit for the default namespace. |
curl https://api.bottlebluellc.com/v1/organizations?platform=bb_trackless \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/organizations",
headers={"x-api-key": API_KEY},
params={"platform": "bb_trackless"},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations?platform=bb_trackless", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations?platform=bb_trackless", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"organizations": [
{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"name": "Acme Corp",
"role": "owner",
"joinedAt": "2026-08-30T12:00:00Z"
}
]
}Get a single organization. Requires membership.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"name": "Acme Corp",
"platform": "bb_trackless",
"ownerEmail": "owner@acme.com",
"createdAt": "2026-08-30T12:00:00Z",
"updatedAt": "2026-08-30T12:00:00Z",
"role": "owner"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}Rename an organization. Requires the admin or owner role.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corporation"}'import requests
resp = requests.patch(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
json={'name': 'Acme Corporation'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Acme Corporation" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Acme Corporation" })
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"name": "Acme Corporation",
"updatedAt": "2026-08-30T12:05:00Z"
}Delete an organization. Requires the owner role.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
force | query | Optional, default false. If the organization still holds data (for example, assets), the delete is refused with 409 ORG_HAS_DATA unless force=true is passed, in which case that data is deleted too. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Also delete every asset stored under this organization
curl "https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07?force=true" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.delete(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
# params={"force": "true"}, # also delete the organization's data
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"deleted": true,
"dataRowsDeleted": 0
}{
"error": "Organization still has 12 data row(s) (e.g. assets); pass ?force=true to delete the organization AND its data",
"code": "ORG_HAS_DATA",
"dataRowCount": 12
}List an organization's members. Requires membership.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"members": [
{ "email": "owner@acme.com", "role": "owner", "joinedAt": "2026-08-30T12:00:00Z", "invitedBy": "owner@acme.com" },
{ "email": "jane@acme.com", "role": "member", "joinedAt": "2026-08-30T12:10:00Z", "invitedBy": "owner@acme.com" }
]
}Add a member to an organization. Requires the admin or owner role. Only an owner can grant the owner role.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@acme.com", "role": "member"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members",
headers={"x-api-key": API_KEY},
json={'email': 'jane@acme.com', 'role': 'member'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com", role: "member" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com", role: "member" })
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"email": "jane@acme.com",
"role": "member",
"joinedAt": "2026-08-30T12:10:00Z"
}{
"error": "This account is already a member of the organization",
"code": "MEMBER_EXISTS"
}Change a member's role. Requires the admin or owner role. Only an owner can change an owner's role or grant it, and the last owner can never be demoted.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@acme.com", "role": "admin"}'import requests
resp = requests.patch(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members",
headers={"x-api-key": API_KEY},
json={'email': 'jane@acme.com', 'role': 'admin'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com", role: "admin" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com", role: "admin" })
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"email": "jane@acme.com",
"role": "admin"
}{
"error": "Cannot remove the last owner; promote another member to owner first",
"code": "LAST_OWNER"
}Remove a member from an organization, or leave it yourself. Removing someone else requires the admin or owner role; leaving is always allowed for your own account, subject to the last-owner rule.
| Parameter | In | Description |
|---|---|---|
orgId | path | required Organization id. |
curl https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members \
-X DELETE \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@acme.com"}'import requests
resp = requests.delete(
"https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members",
headers={"x-api-key": API_KEY},
json={'email': 'jane@acme.com'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "DELETE",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com" })
});
const data = await resp.json();const resp = await fetch("https://api.bottlebluellc.com/v1/organizations/3f1c9e2a7d614b839e5a3f8b1c6d2e07/members", {
method: "DELETE",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email: "jane@acme.com" })
});
const data = await resp.json();{
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07",
"email": "jane@acme.com",
"removed": true
}Weather
Base URL https://api.aquabotany.com/v1
Current conditions plus a 48-hour and 8-day forecast for a coordinate. Units are metric: °C, m/s, hPa, metres, and epoch-second timestamps. The stats block adds derived daylight figures. Sourced from Open-Meteo since 2026-08-02 (previously OpenWeather); the field layout is unchanged. Government weather warnings are not included here. Use /weather/alerts, which serves them from the National Weather Service.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/weather?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"lat": 44.98,
"lon": -93.26,
"timezone": "America/Chicago",
"timezone_offset": -18000,
"current": {
"dt": 1751897400,
"sunrise": 1751885400,
"sunset": 1751939700,
"temp": 22.4,
"feels_like": 22.0,
"pressure": 1013.2,
"humidity": 58,
"dew_point": 13.9,
"uvi": 5.0,
"clouds": 40,
"visibility": 24000,
"wind_speed": 4.1,
"wind_deg": 200,
"wind_gust": 8.2,
"weather": [
{ "id": 802, "main": "Clouds", "description": "partly cloudy", "icon": "03d" }
]
},
"hourly": [ { "dt": 1751897400, "temp": 22.4, "pop": 0.4, "weather": [] } ],
"daily": [ { "dt": 1751864400, "temp": { "min": 12.0, "max": 24.0 }, "pop": 0.8, "weather": [] } ],
"stats": {
"current_time_utc": "2026-07-07 14:30:00",
"sunrise_time_utc": "2026-07-07 11:10:00",
"sunset_time_utc": "2026-07-08 02:15:00",
"total_daylight_hours": 15.08,
"daylight_remaining_seconds": 42300,
"night_duration_seconds": 32100,
"night_remaining_seconds": 0
}
}Active NWS watches, warnings, and advisories covering a point.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/weather/alerts?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/alerts?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/alerts?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/alerts?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"count": 1,
"alerts": [
{
"id": "urn:oid:2.49.0.1.840.0.7f3a9c1e5d2b4a6f8c0e1d3b5a7f9c2e4d6b8a0f.001.1",
"event": "Heat Advisory",
"severity": "Moderate",
"urgency": "Expected",
"certainty": "Likely",
"headline": "Heat Advisory issued July 8 at 3:12AM CDT until July 9 at 8:00PM CDT by NWS Twin Cities/Chanhassen MN",
"area": "Hennepin; Ramsey; Anoka",
"onset": "2026-07-08T12:00:00-05:00",
"expires": "2026-07-09T20:00:00-05:00",
"description": "* WHAT...Heat index values up to 102 expected.\n\n* WHERE...The Twin Cities metro area.\n\n* WHEN...From noon Wednesday to 8 PM CDT Thursday.\n\n* IMPACTS...Hot temperatures and high humidity may cause heat illnesses to occur.",
"instruction": "Drink plenty of fluids, stay in an air-conditioned room, stay out of the sun, and check up on relatives and neighbors."
}
]
}Detailed multi-day (or hourly) NWS forecast for a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
hourly | query | optional 'true' for the hourly forecast. |
curl https://api.aquabotany.com/v1/weather/forecast?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/forecast?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/forecast?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/forecast?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26,
"city": "Minneapolis",
"state": "MN"
},
"office": "MPX",
"type": "daily",
"periods": [
{
"name": "Today",
"start": "2026-07-08T06:00:00-05:00",
"end": "2026-07-08T18:00:00-05:00",
"is_daytime": true,
"temperature": 91,
"temperature_unit": "F",
"wind_speed": "10 to 15 mph",
"wind_direction": "SW",
"short_forecast": "Mostly Sunny",
"detailed_forecast": "Mostly sunny, with a high near 91. Heat index values as high as 100. Southwest wind 10 to 15 mph, with gusts as high as 25 mph.",
"precip_probability": null
},
{
"name": "Tonight",
"start": "2026-07-08T18:00:00-05:00",
"end": "2026-07-09T06:00:00-05:00",
"is_daytime": false,
"temperature": 72,
"temperature_unit": "F",
"wind_speed": "5 to 10 mph",
"wind_direction": "S",
"short_forecast": "Chance Showers And Thunderstorms",
"detailed_forecast": "A chance of showers and thunderstorms after 1am. Mostly cloudy, with a low around 72. Chance of precipitation is 30%.",
"precip_probability": 30
}
]
}Daily historical weather over a date range (ERA5 reanalysis).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
start_date | query | required YYYY-MM-DD. |
end_date | query | required YYYY-MM-DD (span <= 366 days). |
curl https://api.aquabotany.com/v1/weather/history?lat=44.98&lon=-93.26&start_date=2026-06-01&end_date=2026-06-07 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/history?lat=44.98&lon=-93.26&start_date=2026-06-01&end_date=2026-06-07",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/history?lat=44.98&lon=-93.26&start_date=2026-06-01&end_date=2026-06-07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/history?lat=44.98&lon=-93.26&start_date=2026-06-01&end_date=2026-06-07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"start_date": "2026-06-01",
"end_date": "2026-06-07",
"units": {
"time": "iso8601",
"temperature_2m_max": "°C",
"temperature_2m_min": "°C",
"temperature_2m_mean": "°C",
"precipitation_sum": "mm",
"rain_sum": "mm",
"windspeed_10m_max": "km/h"
},
"days": [
{
"date": "2026-06-01",
"temperature_2m_max": 26.4,
"temperature_2m_min": 14.2,
"temperature_2m_mean": 20.1,
"precipitation_sum": 0.0,
"rain_sum": 0.0,
"windspeed_10m_max": 18.7
},
{
"date": "2026-06-02",
"temperature_2m_max": 28.1,
"temperature_2m_min": 15.8,
"temperature_2m_mean": 21.9,
"precipitation_sum": 3.2,
"rain_sum": 3.2,
"windspeed_10m_max": 22.3
}
]
}The latest NWS text product (forecast discussion, roundup, etc.).
| Parameter | In | Description |
|---|---|---|
type | query | optional Product code (default AFD). e.g. RWR, HWO. |
location | query | optional Office or state code (default MPX). e.g. MN. |
curl https://api.aquabotany.com/v1/weather/product \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/product",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/product", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/product", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"type": "AFD",
"location": "MPX",
"name": "Area Forecast Discussion",
"issuing_office": "KMPX",
"issued_at": "2026-07-08T09:12:00+00:00",
"text": "000\nFXUS63 KMPX 080912\nAFDMPX\n\nArea Forecast Discussion\nNational Weather Service Twin Cities/Chanhassen MN\n412 AM CDT Wed Jul 8 2026\n\n.SYNOPSIS...\nHot and increasingly humid conditions build through midweek. Heat\nindex values approach 100 degrees Wednesday afternoon, with a\nchance of showers and thunderstorms Wednesday night as a weak\ncold front approaches from the northwest.\n\n$\n"
}Seasonal gardening advice for the location.
curl https://api.aquabotany.com/v1/weather/seasonal-advice?lat=44.98 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/seasonal-advice?lat=44.98",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/seasonal-advice?lat=44.98", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/seasonal-advice?lat=44.98", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"season": "Summer",
"gardening_tips": [
"Water plants regularly, especially during dry spells.",
"Harvest fruits and vegetables as they ripen.",
"Mulch garden beds to retain moisture and suppress weeds."
]
}Look up the hardiness zone by lat/lon query params.
| Parameter | In | Description |
|---|---|---|
lat | query | optional GET: latitude (with lon). |
lon | query | optional GET: longitude (with lat). |
curl https://api.aquabotany.com/v1/weather/zone?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/weather/zone?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/zone?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/zone?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"zone": "4",
"latitude": 44.98,
"longitude": -93.26
}Look up the hardiness zone by JSON body (zip or lat/lon).
| Parameter | In | Description |
|---|---|---|
lat | query | optional GET: latitude (with lon). |
lon | query | optional GET: longitude (with lat). |
curl https://api.aquabotany.com/v1/weather/zone \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"zip": "55401"}'import requests
resp = requests.post(
"https://api.aquabotany.com/v1/weather/zone",
headers={"x-api-key": API_KEY},
json={'zip': '55401'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/weather/zone", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"zip": "55401"})
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/weather/zone", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"zip": "55401"})
});
const data = await resp.json();{
"zone": "4",
"latitude": 44.979265,
"longitude": -93.273024
}Environment
Base URL https://api.aquabotany.com/v1
Current air quality for a coordinate (US coverage).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
distance | query | Search radius in miles, as a whole number. Defaults to 50. |
curl https://api.aquabotany.com/v1/environment/air?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/air?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/air?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/air?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"timestamp": "2026-07-08 14:32:10",
"pollutants": [
{
"pollutant": "O3",
"aqi": 41,
"category": "Good",
"health_effects": "Air quality is considered satisfactory, and air pollution poses little or no risk."
},
{
"pollutant": "PM2.5",
"aqi": 54,
"category": "Moderate",
"health_effects": "Air quality is acceptable; however, some pollutants may pose a moderate health concern for sensitive groups."
}
],
"overall_aqi": 54,
"overall_category": "Moderate"
}Daily air-quality outlook, global: mean pollutant levels and peak AQI per day.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
days | query | optional Days ahead (1-7, default 3). |
curl https://api.aquabotany.com/v1/environment/air/forecast?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/air/forecast?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/air/forecast?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/air/forecast?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"mode": "forecast",
"days_requested": 3,
"days": [
{
"date": "2026-07-08",
"pm2_5": 7.9,
"pm10": 14.2,
"ozone": 68.3,
"nitrogen_dioxide": 9.6,
"us_aqi": 52,
"european_aqi": 38
},
{
"date": "2026-07-09",
"pm2_5": 9.4,
"pm10": 16.8,
"ozone": 74.1,
"nitrogen_dioxide": 11.2,
"us_aqi": 57,
"european_aqi": 41
},
{
"date": "2026-07-10",
"pm2_5": 6.1,
"pm10": 12.5,
"ozone": 61.9,
"nitrogen_dioxide": 8.3,
"us_aqi": 46,
"european_aqi": 34
}
],
"units": {
"pm2_5": "μg/m³",
"pm10": "μg/m³",
"ozone": "μg/m³",
"nitrogen_dioxide": "μg/m³",
"us_aqi": "USAQI",
"european_aqi": "EAQI"
}
}Daily air-quality history, global (up to 92 days back).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
days | query | optional Days back (1-92, default 7). |
curl https://api.aquabotany.com/v1/environment/air/history?lat=44.98&lon=-93.26&days=2 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/air/history?lat=44.98&lon=-93.26&days=2",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/air/history?lat=44.98&lon=-93.26&days=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/air/history?lat=44.98&lon=-93.26&days=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"mode": "history",
"days_requested": 2,
"days": [
{
"date": "2026-07-06",
"pm2_5": 11.3,
"pm10": 19.7,
"ozone": 71.5,
"nitrogen_dioxide": 12.8,
"us_aqi": 61,
"european_aqi": 44
},
{
"date": "2026-07-07",
"pm2_5": 8.6,
"pm10": 15.1,
"ozone": 65.0,
"nitrogen_dioxide": 10.4,
"us_aqi": 54,
"european_aqi": 39
}
],
"units": {
"pm2_5": "μg/m³",
"pm10": "μg/m³",
"ozone": "μg/m³",
"nitrogen_dioxide": "μg/m³",
"us_aqi": "USAQI",
"european_aqi": "EAQI"
}
}Recent earthquakes, optionally near a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | optional Latitude (filter near a point). |
lon | query | optional Longitude. |
radius_km | query | optional Search radius (default 500). |
min_magnitude | query | optional Minimum magnitude. |
window | query | optional 'day' or 'week' (default week). |
curl https://api.aquabotany.com/v1/environment/earthquakes \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/earthquakes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/earthquakes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/earthquakes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"window": "week",
"near": null,
"min_magnitude": null,
"count": 2,
"earthquakes": [
{
"id": "us7000q3xy",
"magnitude": 5.8,
"place": "42 km SSW of Adak, Alaska",
"time": 1783497600000,
"lat": 51.51,
"lon": -176.91,
"depth_km": 35.2,
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000q3xy",
"tsunami": false
},
{
"id": "us7000q3wa",
"magnitude": 4.6,
"place": "18 km ENE of Kainantu, Papua New Guinea",
"time": 1783463650000,
"lat": -6.24,
"lon": 146.12,
"depth_km": 102.4,
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000q3wa",
"tsunami": false
}
]
}Terrain elevation at a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/environment/elevation?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/elevation?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/elevation?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/elevation?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"elevation_m": 254.0
}Terrain elevation for a named place.
| Parameter | In | Description |
|---|---|---|
name | query | required Place name. |
curl https://api.aquabotany.com/v1/environment/elevation-by-place?name=Kyoto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/elevation-by-place?name=Kyoto",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/elevation-by-place?name=Kyoto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/elevation-by-place?name=Kyoto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"query": "Kyoto",
"resolved": {
"name": "Kyoto",
"lat": 35.02107,
"lon": 135.75385,
"country": "Japan",
"admin1": "Kyōto"
},
"elevation_m": 45.0
}Active natural-hazard events across all EONET natural-event categories: wildfires, severe storms, volcanoes, floods, drought, dust and haze, earthquakes, landslides, sea and lake ice, snow, temperature extremes, water color, and man-made events.
| Parameter | In | Description |
|---|---|---|
category | query | optional EONET category id, one of drought, dustHaze, earthquakes, floods, landslides, manmade, seaLakeIce, severeStorms, snow, tempExtremes, volcanoes, waterColor, wildfires; omit for all categories. |
lat | query | optional Latitude (filter near a point). |
lon | query | optional Longitude. |
radius_km | query | optional Search radius (default 200). |
limit | query | optional Max events (default 20). |
curl https://api.aquabotany.com/v1/environment/events \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/events",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/events", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/events", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"count": 2,
"near": null,
"events": [
{
"id": "EONET_15482",
"title": "Tropical Storm Gilma",
"lat": 14.8,
"lon": -108.2,
"date": "2026-07-08T06:00:00Z",
"link": "https://eonet.gsfc.nasa.gov/api/v3/events/EONET_15482",
"categories": [
"severeStorms"
]
},
{
"id": "EONET_15471",
"title": "Boise Creek Fire, Idaho",
"lat": 44.31,
"lon": -115.62,
"date": "2026-07-07T18:00:00Z",
"link": "https://eonet.gsfc.nasa.gov/api/v3/events/EONET_15471",
"categories": [
"wildfires"
]
}
],
"category": null
}Nearby species observations.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
radius | query | Search radius in kilometres, as a whole number. Defaults to 10. Echoed back as radius_km. |
curl https://api.aquabotany.com/v1/environment/flora-and-fauna?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/flora-and-fauna?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/flora-and-fauna?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/flora-and-fauna?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"latitude": 44.98,
"longitude": -93.26
},
"radius_km": 10,
"observations": [
{
"name": "Sciurus carolinensis",
"common_name": "Eastern Gray Squirrel",
"rank": "species",
"observed_on": "2026-07-07",
"location": "44.9741,-93.2277"
},
{
"name": "Asclepias syriaca",
"common_name": "Common Milkweed",
"rank": "species",
"observed_on": "2026-07-06",
"location": "44.9902,-93.2895"
}
]
}Lunar phase for a date.
| Parameter | In | Description |
|---|---|---|
date | query | optional YYYY-MM-DD; defaults to today. |
curl https://api.aquabotany.com/v1/environment/moon \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/moon",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/moon", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/moon", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"date": "2026-07-08",
"phase": "Last Quarter",
"age_days": 23.32,
"illumination": 0.3764,
"illumination_percent": 37.6,
"is_waxing": false,
"previous_new_moon": "2026-06-15",
"next_new_moon": "2026-07-14",
"next_full_moon": "2026-07-29"
}Current airborne pollen levels by type (Europe coverage).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/environment/pollen?lat=52.52&lon=13.41 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/pollen?lat=52.52&lon=13.41",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen?lat=52.52&lon=13.41", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen?lat=52.52&lon=13.41", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 52.52,
"lon": 13.41
},
"observed_at": "2026-07-08T16:15",
"unit": "grains/m³",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 34.6,
"severity": "moderate"
},
"mugwort": {
"value": 2.1,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 1.8,
"severity": "low"
}
},
"coverage": "Europe (CAMS); null outside the model domain"
}Daily pollen outlook: each type's peak level with a severity band (Europe coverage).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
days | query | optional Days ahead (1-7, default 3). |
curl https://api.aquabotany.com/v1/environment/pollen/forecast?lat=52.52&lon=13.41 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/pollen/forecast?lat=52.52&lon=13.41",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen/forecast?lat=52.52&lon=13.41", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen/forecast?lat=52.52&lon=13.41", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 52.52,
"lon": 13.41
},
"mode": "forecast",
"days_requested": 3,
"days": [
{
"date": "2026-07-08",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 58.2,
"severity": "high"
},
"mugwort": {
"value": 3.4,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 2.9,
"severity": "low"
}
}
},
{
"date": "2026-07-09",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 41.7,
"severity": "moderate"
},
"mugwort": {
"value": 4.8,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 3.6,
"severity": "low"
}
}
},
{
"date": "2026-07-10",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 24.3,
"severity": "moderate"
},
"mugwort": {
"value": 2.2,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 1.5,
"severity": "low"
}
}
}
],
"unit": "grains/m³",
"coverage": "Europe (CAMS); null outside the model domain"
}Daily pollen history, up to 92 days back (Europe coverage).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
days | query | optional Days back (1-92, default 7). |
curl https://api.aquabotany.com/v1/environment/pollen/history?lat=52.52&lon=13.41&days=2 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/pollen/history?lat=52.52&lon=13.41&days=2",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen/history?lat=52.52&lon=13.41&days=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/pollen/history?lat=52.52&lon=13.41&days=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 52.52,
"lon": 13.41
},
"mode": "history",
"days_requested": 2,
"days": [
{
"date": "2026-07-06",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 47.9,
"severity": "moderate"
},
"mugwort": {
"value": 2.7,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 2.0,
"severity": "low"
}
}
},
{
"date": "2026-07-07",
"pollen": {
"alder": {
"value": 0.0,
"severity": "none"
},
"birch": {
"value": 0.0,
"severity": "none"
},
"grass": {
"value": 52.4,
"severity": "high"
},
"mugwort": {
"value": 3.1,
"severity": "low"
},
"olive": {
"value": 0.0,
"severity": "none"
},
"ragweed": {
"value": 2.4,
"severity": "low"
}
}
}
],
"unit": "grains/m³",
"coverage": "Europe (CAMS); null outside the model domain"
}Soil classification (WRB) for a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/environment/soil/composition?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/soil/composition?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/composition?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/composition?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"soil_class": "Luvisols",
"top_classes": [
{
"class": "Luvisols",
"probability_percent": 31
},
{
"class": "Phaeozems",
"probability_percent": 23
},
{
"class": "Cambisols",
"probability_percent": 14
},
{
"class": "Retisols",
"probability_percent": 9
},
{
"class": "Chernozems",
"probability_percent": 7
}
],
"reference_system": "WRB (World Reference Base)"
}Live soil temperature and moisture at several depths.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/environment/soil/moisture?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/soil/moisture?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/moisture?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/moisture?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"observed_at": "2026-07-08T09:15",
"temperature": {
"unit": "°C",
"by_depth": {
"0cm": 24.3,
"6cm": 22.1,
"18cm": 20.4,
"54cm": 16.8
}
},
"moisture": {
"unit": "m³/m³",
"by_depth": {
"0_to_1cm": 0.28,
"1_to_3cm": 0.291,
"3_to_9cm": 0.305,
"9_to_27cm": 0.312,
"27_to_81cm": 0.334
}
}
}Numeric soil properties for a point. soil_properties passes through every numeric layer ISRIC SoilGrids publishes for the location, keyed by SoilGrids layer name (the example shows clay, sand, phh2o, and soc); each property carries its unit and mean values per depth band, already scaled to the stated unit.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/environment/soil/properties?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/soil/properties?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/properties?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/soil/properties?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"soil_properties": {
"clay": {
"unit": "g/kg",
"depths": {
"0-5cm": 25.4,
"5-15cm": 26.1,
"15-30cm": 27.8
}
},
"sand": {
"unit": "g/kg",
"depths": {
"0-5cm": 38.2,
"5-15cm": 37.5,
"15-30cm": 36.9
}
},
"phh2o": {
"unit": "pH",
"depths": {
"0-5cm": 6.4,
"5-15cm": 6.5,
"15-30cm": 6.7
}
},
"soc": {
"unit": "g/kg",
"depths": {
"0-5cm": 41.7,
"5-15cm": 28.3,
"15-30cm": 17.2
}
}
}
}Active wildfire events, optionally near a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | optional Latitude (filter near a point). |
lon | query | optional Longitude. |
radius_km | query | optional Search radius (default 200). |
limit | query | optional Max events (default 20). |
curl https://api.aquabotany.com/v1/environment/wildfire \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/environment/wildfire",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/environment/wildfire", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/environment/wildfire", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"count": 2,
"near": null,
"events": [
{
"id": "EONET_15471",
"title": "Boise Creek Fire, Idaho",
"lat": 44.31,
"lon": -115.62,
"date": "2026-07-07T18:00:00Z",
"link": "https://eonet.gsfc.nasa.gov/api/v3/events/EONET_15471"
},
{
"id": "EONET_15465",
"title": "Rock Springs Fire, Nevada",
"lat": 40.87,
"lon": -117.45,
"date": "2026-07-06T12:00:00Z",
"link": "https://eonet.gsfc.nasa.gov/api/v3/events/EONET_15465"
}
]
}Nearby recycling drop-off locations.
curl https://api.aquabotany.com/v1/sustainability/recycling-locations \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/sustainability/recycling-locations",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/sustainability/recycling-locations", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/sustainability/recycling-locations", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"name": "Minneapolis Recycling Drop-Off Facility",
"address": "1400 W 96th Street, Bloomington, MN 55431",
"website": "https://www.hennepin.us/dropoff",
"hours": "Tuesday-Saturday, 9 AM - 5 PM",
"materials_accepted": [
"Electronics",
"Batteries",
"Cardboard",
"Household hazardous waste"
],
"notes": "Free drop-off for Minneapolis residents."
}Sustainability and eco tips.
curl https://api.aquabotany.com/v1/sustainability/tips?material=plastic \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/sustainability/tips?material=plastic",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/sustainability/tips?material=plastic", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/sustainability/tips?material=plastic", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"description": "Plastics can usually be recycled if they are clean and have a recycling code (1-7).",
"instructions": [
"Check for the recycling triangle with a number (1-7).",
"Clean and dry plastics before recycling.",
"Avoid recycling plastic bags; take them to a designated drop-off."
],
"examples": [
"Bottles, containers, food packaging",
"HDPE pipes",
"Polystyrene trays"
],
"notes": "Some plastics like styrofoam, PVC, and polystyrene are not recyclable curbside."
}Location & places
Base URL https://api.aquabotany.com/v1
Resolve a place name to coordinates.
| Parameter | In | Description |
|---|---|---|
name | query | required Town, city, or landmark. |
count | query | optional Max results (1-10, default 5). |
curl https://api.aquabotany.com/v1/location/geocode?name=Kyoto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/location/geocode?name=Kyoto",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/location/geocode?name=Kyoto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/location/geocode?name=Kyoto", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"query": "Kyoto",
"count": 1,
"results": [
{
"name": "Kyoto",
"lat": 35.02107,
"lon": 135.75385,
"country": "Japan",
"country_code": "JP",
"admin1": "Kyōto",
"admin2": "Kyōto",
"timezone": "Asia/Tokyo",
"population": 1463723,
"elevation_m": 50.0
}
]
}Resolve a coordinate to the nearest address.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/v1/location/reverse-geocode?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/location/reverse-geocode?lat=44.98&lon=-93.26",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/location/reverse-geocode?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/location/reverse-geocode?lat=44.98&lon=-93.26", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"location": {
"lat": 44.98,
"lon": -93.26
},
"display_name": "Downtown West, Minneapolis, Hennepin County, Minnesota, 55403, United States",
"address": {
"city": "Minneapolis",
"county": "Hennepin County",
"state": "Minnesota",
"postcode": "55403",
"country": "United States",
"country_code": "us"
}
}Nearest gas stations (the Kwik Trip / Kwik Star network) to a coordinate. Locations only: id, name, distance, coordinates, address, and phone. No fuel price fields. Also reachable at its original path, /places/kwik-trip, which remains a live alias.
Stations come from a packaged snapshot of the network, so the response is a station locator, not a live price feed: source is kwiktrip-snapshot and data_as_of is the snapshot date. The former path /places/kwik-trip remains a working alias for backward compatibility and returns the same response.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
radius_km | query | optional Only stores within this radius. |
limit | query | optional Max stores (default 10, max 50). |
curl https://api.aquabotany.com/v1/places/gas-stations?lat=44.98&lon=-93.26&limit=2 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/places/gas-stations?lat=44.98&lon=-93.26&limit=2",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/places/gas-stations?lat=44.98&lon=-93.26&limit=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/places/gas-stations?lat=44.98&lon=-93.26&limit=2", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"near": {
"lat": 44.98,
"lon": -93.26,
"radius_km": null
},
"count": 2,
"stores": [
{
"id": 411,
"name": "KWIK TRIP #411",
"distance_km": 15.4,
"lat": 44.99722,
"lon": -93.45473,
"address": {
"street": "1605 ANNAPOLIS LN N",
"city": "PLYMOUTH",
"state": "MN",
"zip": 55441
},
"phone": "(763) 577-1983"
},
{
"id": 896,
"name": "KWIK TRIP #896",
"distance_km": 16.3,
"lat": 45.12528,
"lon": -93.23671,
"address": {
"street": "1355 85TH AVE NE",
"city": "BLAINE",
"state": "MN",
"zip": 55434
},
"phone": "(763) 717-2807"
}
],
"data_as_of": "2026-07-06"
}Plants
Base URL https://api.aquabotany.com/v1
List the caller's plants.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/my-plants \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/my-plants?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/my-plants",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/my-plants",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"plants": [
{
"id": "3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34",
"name": "Basil",
"environmentId": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810",
"quantity": 1.0,
"createdAt": "2026-07-08T14:21:37.412903+00:00",
"updatedAt": "2026-07-08T14:21:37.412903+00:00",
"details": {
"species": "Ocimum basilicum"
}
},
{
"id": "9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46",
"name": "Monstera",
"environmentId": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810",
"quantity": 2.0,
"createdAt": "2026-06-30T09:05:12.038471+00:00",
"updatedAt": "2026-07-05T18:47:03.559214+00:00",
"details": {}
}
],
"environments": [
{
"id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810",
"name": "Kitchen Window"
}
]
}Add a plant to the collection.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/my-plants \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/my-plants \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/my-plants",
headers={"x-api-key": API_KEY},
json={'name': 'Basil', 'species': 'Ocimum basilicum', 'environment_id': '7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/my-plants",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Basil', 'species': 'Ocimum basilicum', 'environment_id': '7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Basil", "species": "Ocimum basilicum", "environment_id": "7b1e6c2d-05a9-4f3e-8d21-c4a9e7f2b810", "platform": "my_garden"})
});{
"message": "Plant added successfully",
"plant_id": "3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34"
}Update or remove a plant in the collection.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Basil", "quantity": 2, "location": "kitchen windowsill"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Basil", "quantity": 2, "location": "kitchen windowsill", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34",
headers={"x-api-key": API_KEY},
json={'name': 'Basil', 'quantity': 2, 'location': 'kitchen windowsill'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Basil', 'quantity': 2, 'location': 'kitchen windowsill', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Basil", "quantity": 2, "location": "kitchen windowsill"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Basil", "quantity": 2, "location": "kitchen windowsill", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Basil", "quantity": 2, "location": "kitchen windowsill"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Basil", "quantity": 2, "location": "kitchen windowsill", "platform": "my_garden"})
});{
"message": "Plant updated successfully",
"updated_attributes": {
"Name": "Basil",
"Quantity": 2.0,
"Details": {
"species": "Ocimum basilicum",
"location": "kitchen windowsill"
},
"UpdatedAt": "2026-07-08T15:02:44.918274+00:00"
}
}Update or remove a plant in the collection.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Plant deleted successfully"
}Find plants matching criteria.
curl https://api.aquabotany.com/v1/plants/plantfinder?query_type=zone&filter_value=4 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/plants/plantfinder?query_type=zone&filter_value=4",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/plants/plantfinder?query_type=zone&filter_value=4", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/plants/plantfinder?query_type=zone&filter_value=4", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"CommonName": "Bee Balm",
"Zone": "4"
},
{
"CommonName": "Purple Coneflower",
"Zone": "4"
}
]Plant-care tips.
| Parameter | In | Description |
|---|---|---|
plant | query | required Plant type: succulent, fern, cactus, herbs, orchid, peace lily, monstera, spider plant, or bonsai. |
curl https://api.aquabotany.com/v1/plants/tips?plant=monstera \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/plants/tips?plant=monstera",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/plants/tips?plant=monstera", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/plants/tips?plant=monstera", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"watering": "Water every 1-2 weeks; keep soil lightly moist.",
"light": "Bright, indirect light.",
"humidity": "Moderate to high humidity.",
"notes": "Wipe leaves occasionally to remove dust and promote growth."
}Smart garden
Base URL https://api.aquabotany.com/v1
Registered garden automation devices.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/automation-devices \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/automation-devices?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/automation-devices",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/automation-devices",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"devices": [
{
"id": "b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
"name": "Drip valve",
"assignedTo": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13",
"environment": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
"serial": "DV-00142",
"type": "Device",
"config": {}
}
],
"deviceTypes": [
"Type 1",
"Type 2"
],
"selectedDeviceType": "Type 1",
"plants": [
{
"id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13",
"name": "Basil",
"environment": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47"
}
],
"environments": [
{
"id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
"name": "Backyard bed"
}
]
}Registered garden automation devices.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/automation-devices \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/automation-devices \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/automation-devices",
headers={"x-api-key": API_KEY},
json={'name': 'Drip valve', 'environment_id': 'c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47', 'plant_id': 'e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13', 'serial': 'DV-00142', 'config': {}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/automation-devices",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Drip valve', 'environment_id': 'c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47', 'plant_id': 'e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13', 'serial': 'DV-00142', 'config': {}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Drip valve", "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", "plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13", "serial": "DV-00142", "config": {}, "platform": "my_garden"})
});{
"message": "Device added successfully",
"device_id": "b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
"environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
"plant_id": "e4b1c8a6-2d93-4f70-b1e5-6c8a2d4f9b13",
"platform": "my_garden"
}Update or remove a device.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Drip valve", "serial": "DV-00142", "config": {}}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Drip valve", "serial": "DV-00142", "config": {}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
json={'name': 'Drip valve', 'serial': 'DV-00142', 'config': {}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Drip valve', 'serial': 'DV-00142', 'config': {}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Drip valve", "serial": "DV-00142", "config": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Drip valve", "serial": "DV-00142", "config": {}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Drip valve", "serial": "DV-00142", "config": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Drip valve", "serial": "DV-00142", "config": {}, "platform": "my_garden"})
});{
"message": "Device updated successfully"
}Update or remove a device.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Device deleted successfully"
}Queue or list commands for a device.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"deviceId": "b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
"commandId": "7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58",
"command": "open_valve",
"timestamp": "2026-07-08T09:15:32.418206"
},
{
"deviceId": "b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
"commandId": "2c5d8f4b-1a97-4e63-b0d8-7f3e6a1c9b25",
"command": "close_valve",
"timestamp": "2026-07-08T10:02:11.907414"
}
]Queue or list commands for a device.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "open_valve"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "open_valve", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
json={'command': 'open_valve'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'command': 'open_valve', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"command": "open_valve"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"command": "open_valve", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"command": "open_valve"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"command": "open_valve", "platform": "my_garden"})
});{
"message": "Command created",
"commandId": "7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58"
}Delete a queued device command.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Command deleted"
}Garden environment readings / zones.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/garden-environment \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/garden-environment?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/garden-environment",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/garden-environment",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"EnvironmentID": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
"Name": "Backyard bed",
"CreatedAt": "2026-07-08T09:15:32.418206+00:00",
"UpdatedAt": "2026-07-08T09:15:32.418206+00:00",
"Details": {
"zone": "4b"
},
"id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47"
},
{
"EnvironmentID": "f6d2b9e4-8a15-4c73-9e0b-3d7f5a2c8e61",
"Name": "Greenhouse",
"CreatedAt": "2026-07-01T14:22:05.113842+00:00",
"UpdatedAt": "2026-07-05T08:47:19.664501+00:00",
"Details": {
"zone": "4b",
"humidity_target": 65
},
"id": "f6d2b9e4-8a15-4c73-9e0b-3d7f5a2c8e61"
}
]Garden environment readings / zones.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/garden-environment \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Backyard bed", "zone": "4b"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/garden-environment \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Backyard bed", "zone": "4b", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/garden-environment",
headers={"x-api-key": API_KEY},
json={'name': 'Backyard bed', 'zone': '4b'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/garden-environment",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Backyard bed', 'zone': '4b', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Backyard bed", "zone": "4b"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Backyard bed", "zone": "4b", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Backyard bed", "zone": "4b"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Backyard bed", "zone": "4b", "platform": "my_garden"})
});{
"message": "Environment added successfully",
"environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47"
}Update or delete a garden environment.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Backyard bed", "zone": "5a"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Backyard bed", "zone": "5a", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
json={'name': 'Backyard bed', 'zone': '5a'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'name': 'Backyard bed', 'zone': '5a', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Backyard bed", "zone": "5a"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Backyard bed", "zone": "5a", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Backyard bed", "zone": "5a"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"name": "Backyard bed", "zone": "5a", "platform": "my_garden"})
});{
"message": "Environment updated successfully",
"updated_attributes": {
"Name": "Backyard bed",
"Details": {
"zone": "5a"
},
"UpdatedAt": "2026-07-08T10:02:11.907414+00:00"
}
}Update or delete a garden environment.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Environment with ID c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47 deleted successfully"
}Submit or read garden sensor readings.
| Parameter | In | Description |
|---|---|---|
deviceId | query | optional Restrict to one device serial. |
startKey | query | optional Opaque pagination token from the previous page's nextKey. Pass it back verbatim; a nextKey of null means the last page. |
curl https://api.aquabotany.com/v1/sensor-data?deviceId=SN-00142 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/sensor-data?deviceId=SN-00142",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/sensor-data?deviceId=SN-00142", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/sensor-data?deviceId=SN-00142", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"Items": [
{
"deviceId": "SN-00142",
"ts": 1783503000,
"sensorType": "moisture",
"value": 42.0
},
{
"deviceId": "SN-00142",
"ts": 1783499400,
"sensorType": "temp_c",
"value": 21.5
}
],
"nextKey": null
}Submit or read garden sensor readings.
curl https://api.aquabotany.com/v1/sensor-data \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"serial": "SN-00142", "sensordata": [{"timestamp": 1783674000, "type": "soilmoisture", "value": 41.5}, {"timestamp": 1783674600, "type": "temp", "value": 22.4}]}'import requests
resp = requests.post(
"https://api.aquabotany.com/v1/sensor-data",
headers={"x-api-key": API_KEY},
json={'serial': 'SN-00142', 'sensordata': [{'timestamp': 1783674000, 'type': 'soilmoisture', 'value': 41.5}, {'timestamp': 1783674600, 'type': 'temp', 'value': 22.4}]},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/sensor-data", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"serial": "SN-00142", "sensordata": [{"timestamp": 1783674000, "type": "soilmoisture", "value": 41.5}, {"timestamp": 1783674600, "type": "temp", "value": 22.4}]})
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/sensor-data", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"serial": "SN-00142", "sensordata": [{"timestamp": 1783674000, "type": "soilmoisture", "value": 41.5}, {"timestamp": 1783674600, "type": "temp", "value": 22.4}]})
});
const data = await resp.json();{
"msg": "Accepted 2 readings 🌱"
}Tasks & reminders
Base URL https://api.aquabotany.com/v1
Reminders (legacy collection).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/reminders \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/reminders?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/reminders",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/reminders",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/reminders?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/reminders?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"reminderId": "9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
"reminderText": "Water the ferns",
"dueEpoch": 1783674000,
"status": "PENDING",
"created": "2026-07-08T09:15:32+00:00",
"lastModified": "2026-07-08T09:15:32+00:00"
},
{
"reminderId": "4a8e1c6f2b9d4073a5e8c1f6d2b94037",
"reminderText": "Check drip lines",
"dueEpoch": 1783760400,
"status": "PENDING",
"created": "2026-07-08T09:20:04+00:00",
"lastModified": "2026-07-08T09:20:04+00:00"
}
]Reminders (legacy collection).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/reminders \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/reminders \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/reminders",
headers={"x-api-key": API_KEY},
json={'reminderText': 'Water the ferns', 'dueDateTime': '2026-07-10T09:00:00Z'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'reminderText': 'Water the ferns', 'dueDateTime': '2026-07-10T09:00:00Z', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"reminderText": "Water the ferns", "dueDateTime": "2026-07-10T09:00:00Z", "platform": "my_garden"})
});{
"reminderId": "9f2c4b7d8a1e4f60b3c5d7e9a2f14c86"
}Read, update, or delete a reminder.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"reminderId": "9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
"reminderText": "Water the ferns",
"dueEpoch": 1783674000,
"status": "PENDING",
"created": "2026-07-08T09:15:32+00:00",
"lastModified": "2026-07-08T09:15:32+00:00"
}Read, update, or delete a reminder.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reminderText": "Water the ferns and orchids", "status": "COMPLETED"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reminderText": "Water the ferns and orchids", "status": "COMPLETED", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
json={'reminderText': 'Water the ferns and orchids', 'status': 'COMPLETED'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'reminderText': 'Water the ferns and orchids', 'status': 'COMPLETED', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"reminderText": "Water the ferns and orchids", "status": "COMPLETED"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"reminderText": "Water the ferns and orchids", "status": "COMPLETED", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"reminderText": "Water the ferns and orchids", "status": "COMPLETED"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"reminderText": "Water the ferns and orchids", "status": "COMPLETED", "platform": "my_garden"})
});{
"reminderId": "9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
"reminderText": "Water the ferns and orchids",
"dueEpoch": 1783674000,
"status": "COMPLETED",
"created": "2026-07-08T09:15:32+00:00",
"lastModified": "2026-07-08T11:42:05+00:00"
}Read, update, or delete a reminder.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});(no response body)List gardening tasks.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/tasks?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/tasks",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/tasks",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
"action_type": "water",
"source": "manual",
"details": {
"plant": "Tomatoes",
"amount_ml": 500
},
"schedule_time": "2026-07-10T09:00:00Z",
"created_at": "2026-07-08T09:15:32.418206",
"completed": false
},
{
"task_id": "a8c3e6f1-9d24-4b7e-8f05-3a6c9e2d5b71",
"action_type": "fertilize",
"source": "assistant",
"details": {},
"schedule_time": null,
"created_at": "2026-07-07T18:40:11.203984",
"completed": true
}
]Create a gardening task.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/tasks",
headers={"x-api-key": API_KEY},
json={'action_type': 'water', 'source': 'manual', 'schedule_time': '2026-07-10T09:00:00Z', 'details': {'plant': 'Tomatoes', 'amount_ml': 500}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/tasks",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'action_type': 'water', 'source': 'manual', 'schedule_time': '2026-07-10T09:00:00Z', 'details': {'plant': 'Tomatoes', 'amount_ml': 500}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"action_type": "water", "source": "manual", "schedule_time": "2026-07-10T09:00:00Z", "details": {"plant": "Tomatoes", "amount_ml": 500}, "platform": "my_garden"})
});{
"message": "Task created successfully",
"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39"
}Mark a gardening task completed.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/completed \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks/completed \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/tasks/completed",
headers={"x-api-key": API_KEY},
json={'task_id': 'd1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39', 'source': 'manual'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/tasks/completed",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'task_id': 'd1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39', 'source': 'manual', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/completed", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/completed", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/completed", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/completed", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", "source": "manual", "platform": "my_garden"})
});{
"status": "completed"
}List the caller's reminders, soonest-due first.
| Parameter | In | Description |
|---|---|---|
status | query | optional GET filter: scheduled|done|cancelled. |
limit | query | optional GET: max results (default 50, max 200). |
id | query | optional DELETE: id of the reminder to delete. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/reminders \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/tasks/reminders?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"count": 2,
"reminders": [
{
"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57",
"title": "Prune roses",
"notes": "morning",
"due_at": "2026-07-10T09:00:00+00:00",
"created_at": "2026-07-08T09:15:32.418206+00:00",
"updated_at": "2026-07-08T09:15:32.418206+00:00",
"status": "scheduled"
},
{
"id": "1d7f3a92-6e48-4c05-9b2a-e5c803d61f74",
"title": "Fertilize tomatoes",
"notes": "",
"due_at": "2026-07-12T15:30:00+00:00",
"created_at": "2026-07-08T09:20:04.771203+00:00",
"updated_at": "2026-07-08T09:20:04.771203+00:00",
"status": "scheduled"
}
]
}Create a reminder.
| Parameter | In | Description |
|---|---|---|
status | query | optional GET filter: scheduled|done|cancelled. |
limit | query | optional GET: max results (default 50, max 200). |
id | query | optional DELETE: id of the reminder to delete. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/reminders \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks/reminders \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
json={'title': 'Prune roses', 'due_at': '2026-07-10T09:00:00Z', 'notes': 'morning'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Prune roses', 'due_at': '2026-07-10T09:00:00Z', 'notes': 'morning', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Prune roses", "due_at": "2026-07-10T09:00:00Z", "notes": "morning", "platform": "my_garden"})
});{
"reminder": {
"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57",
"title": "Prune roses",
"notes": "morning",
"due_at": "2026-07-10T09:00:00+00:00",
"created_at": "2026-07-08T09:15:32.418206+00:00",
"updated_at": "2026-07-08T09:15:32.418206+00:00",
"status": "scheduled"
}
}Update a reminder (including marking it done).
| Parameter | In | Description |
|---|---|---|
status | query | optional GET filter: scheduled|done|cancelled. |
limit | query | optional GET: max results (default 50, max 200). |
id | query | optional DELETE: id of the reminder to delete. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/reminders \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks/reminders \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
json={'id': '5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57', 'status': 'done'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'id': '5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57', 'status': 'done', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", "status": "done", "platform": "my_garden"})
});{
"reminder": {
"id": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57",
"title": "Prune roses",
"notes": "morning",
"due_at": "2026-07-10T09:00:00+00:00",
"created_at": "2026-07-08T09:15:32.418206+00:00",
"updated_at": "2026-07-08T11:42:05.771203+00:00",
"status": "done"
}
}Delete a reminder (by ?id=).
| Parameter | In | Description |
|---|---|---|
status | query | optional GET filter: scheduled|done|cancelled. |
limit | query | optional GET: max results (default 50, max 200). |
id | query | optional DELETE: id of the reminder to delete. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57&platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
"https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57&platform=my_garden",
headers={"x-api-key": API_KEY},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57&platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/reminders?id=5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57&platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"deleted": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57"
}Scheduled (upcoming) tasks.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15&platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
"https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15&platform=my_garden",
headers={"x-api-key": API_KEY},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15&platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/scheduled?start_date=2026-07-08&end_date=2026-07-15&platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
"action_type": "water",
"source": "manual",
"details": {
"plant": "Tomatoes",
"amount_ml": 500
},
"schedule_time": "2026-07-10T09:00:00Z",
"created_at": "2026-07-08T09:15:32.418206",
"completed": false
}
]Get a single task. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
"action_type": "water",
"source": "manual",
"details": {
"plant": "Tomatoes",
"amount_ml": 500
},
"schedule_time": "2026-07-10T09:00:00Z",
"created_at": "2026-07-08T09:15:32.418206",
"completed": false
}Update a task's fields.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
json={'action_type': 'water', 'schedule_time': '2026-07-10T18:00:00Z'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'action_type': 'water', 'schedule_time': '2026-07-10T18:00:00Z', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"action_type": "water", "schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"})
});{
"status": "updated"
}Update a task's fields (alias of PUT).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule_time": "2026-07-10T18:00:00Z"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
json={'schedule_time': '2026-07-10T18:00:00Z'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'schedule_time': '2026-07-10T18:00:00Z', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"schedule_time": "2026-07-10T18:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"schedule_time": "2026-07-10T18:00:00Z"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"schedule_time": "2026-07-10T18:00:00Z", "platform": "my_garden"})
});{
"status": "updated"
}Delete a task.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted"
}Community
Base URL https://api.aquabotany.com/v1
List community articles.
Rows omit content, so a browse stays cheap; read one article by id for its body.
| Parameter | In | Description |
|---|---|---|
platform | query | Optional. Namespace to read. Omitted means your default namespace; * reads across every platform you have. |
category | query | Optional. Exact match on the article's category. Category is required at create time, so every article has one. |
since | query | Optional. ISO timestamp. Returns articles created at or after it. |
authorId | query | Optional. One author. Accepts either the opaque authorId returned in responses or the author's login email. |
limit | query | Optional. Rows per page, 1 to 1000. Defaults to 20. |
startKey | query | Optional. The nextKey from a previous response, to fetch the next page. |
Filters are applied before the page is cut, so a page holds limit matching rows and nextKey refers to the filtered sequence. Before 2026-09-06 the order was reversed: asking for 20 could return 3, and a caller walking pages could not tell a thin page from the end of the data. Clients written against the old behavior keep working, they simply stop seeing short pages.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/articles?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"
# One category, filtered server side
curl "https://api.aquabotany.com/v1/articles?platform=my_garden&category=herbs&limit=50" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/articles",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/articles",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)
# One category, filtered server side
resp = requests.get(
"https://api.aquabotany.com/v1/articles",
headers={"x-api-key": API_KEY},
params={"platform": "my_garden", "category": "herbs", "limit": 50},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// One category, filtered server side
const herbs = await fetch(
"https://api.aquabotany.com/v1/articles?platform=my_garden&category=herbs&limit=50",
{ method: "GET", headers: { "x-api-key": API_KEY } }
);// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// One category, filtered server side
const herbs = await fetch(
"https://api.aquabotany.com/v1/articles?platform=my_garden&category=herbs&limit=50",
{ method: "GET", headers: { "x-api-key": API_KEY } }
);{
"items": [
{
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"title": "My tomato season",
"category": "vegetables",
"thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg",
"authorId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"created": "2026-07-08T13:05:41Z",
"likeCount": 3,
"shareCount": 1,
"commentCount": 2
},
{
"articleId": "b7d3f0c2-4a1e-4f6b-9c8d-2e5a7f0b3c61",
"title": "Overwintering rosemary indoors",
"category": "herbs",
"thumbnail": null,
"authorId": "u_1a2b3c4d5e6f7a8b", "isYou": false,
"created": "2026-07-06T09:41:33Z",
"likeCount": 5,
"shareCount": 2,
"commentCount": 4
}
],
"nextKey": null
}Publish an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/articles",
headers={"x-api-key": API_KEY},
json={'title': 'My tomato season', 'content': 'How I staked, pruned, and watered my way to a July harvest.', 'category': 'vegetables', 'thumbnail': 'https://cdn.aquabotany.com/thumbs/tomato-season.jpg'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/articles",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'My tomato season', 'content': 'How I staked, pruned, and watered my way to a July harvest.', 'category': 'vegetables', 'thumbnail': 'https://cdn.aquabotany.com/thumbs/tomato-season.jpg', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "My tomato season", "content": "How I staked, pruned, and watered my way to a July harvest.", "category": "vegetables", "thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg", "platform": "my_garden"})
});{
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83"
}Read, update, or delete an article. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"title": "My tomato season",
"content": "How I staked, pruned, and watered my way to a July harvest.",
"category": "vegetables",
"thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg",
"authorId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"created": "2026-07-08T13:05:41Z",
"likeCount": 3,
"shareCount": 1,
"commentCount": 2
}Read, update, or delete an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "My tomato season, revised"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "My tomato season, revised", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
json={'title': 'My tomato season, revised'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'My tomato season, revised', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "My tomato season, revised"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "My tomato season, revised", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "My tomato season, revised"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "My tomato season, revised", "platform": "my_garden"})
});{
"message": "updated"
}Read, update, or delete an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Updated text"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Updated text", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
json={'content': 'Updated text'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'content': 'Updated text', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"content": "Updated text"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"content": "Updated text", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"content": "Updated text"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"content": "Updated text", "platform": "my_garden"})
});{
"message": "updated"
}Read, update, or delete an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{}Comments on an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"commentId": "5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"userId": "u_1a2b3c4d5e6f7a8b", "isYou": false,
"body": "Great tips!",
"created": "2026-07-08T14:12:09Z",
"edited": false
},
{
"commentId": "0c3d7a9e-2f5b-4d8c-b1e6-4a7f9c2d5e80",
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"userId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"body": "Which variety did you grow?",
"created": "2026-07-08T15:03:44Z",
"edited": false
}
],
"nextKey": null
}Comments on an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Great tips!"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Great tips!", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
json={'body': 'Great tips!'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'body': 'Great tips!', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"body": "Great tips!"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"body": "Great tips!", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"body": "Great tips!"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"body": "Great tips!", "platform": "my_garden"})
});{
"commentId": "5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27"
}Update or delete a comment.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Great tips! Edited."}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Great tips! Edited.", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
json={'body': 'Great tips! Edited.'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'body': 'Great tips! Edited.', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"body": "Great tips! Edited."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"body": "Great tips! Edited.", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"body": "Great tips! Edited."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"body": "Great tips! Edited.", "platform": "my_garden"})
});{
"message": "updated"
}Update or delete a comment.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{}Likes on an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"userId": "u_1a2b3c4d5e6f7a8b", "isYou": false,
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"likedAt": "2026-07-08T13:47:20Z"
},
{
"userId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"likedAt": "2026-07-08T14:30:52Z"
}
],
"nextKey": null
}Like an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "my_garden"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "my_garden", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
json={'platform': 'my_garden'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'platform': 'my_garden', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"platform": "my_garden"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"platform": "my_garden", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"platform": "my_garden"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"platform": "my_garden", "platform": "my_garden"})
});{}Remove a like.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{}Engagement stats for an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/stats?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"likeCount": 3,
"shareCount": 1,
"commentCount": 2
}Recent comments across articles.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/comments \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/comments?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/comments",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/comments",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/comments", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/comments?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/comments", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/comments?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"commentId": "0c3d7a9e-2f5b-4d8c-b1e6-4a7f9c2d5e80",
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"userId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"body": "Which variety did you grow?",
"created": "2026-07-08T15:03:44Z",
"edited": false
}
],
"nextKey": null
}The community activity feed.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/feed \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/feed?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/feed",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/feed",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/feed", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/feed?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/feed", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/feed?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"title": "My tomato season",
"content": "How I staked, pruned, and watered my way to a July harvest.",
"category": "vegetables",
"thumbnail": "https://cdn.aquabotany.com/thumbs/tomato-season.jpg",
"authorId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"created": "2026-07-08T13:05:41Z",
"likeCount": 3,
"shareCount": 1,
"commentCount": 2
},
{
"articleId": "b7d3f0c2-4a1e-4f6b-9c8d-2e5a7f0b3c61",
"title": "Overwintering rosemary indoors",
"content": "Light, airflow, and restrained watering keep rosemary alive until spring.",
"category": "herbs",
"thumbnail": null,
"authorId": "u_1a2b3c4d5e6f7a8b", "isYou": false,
"created": "2026-07-06T09:41:33Z",
"likeCount": 5,
"shareCount": 2,
"commentCount": 4
}
],
"nextKey": null
}The caller's likes.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/likes \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/likes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/likes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/likes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/likes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/likes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/likes", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/likes?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"items": [
{
"userId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
"likedAt": "2026-07-08T14:30:52Z"
}
],
"nextKey": null
}Gamification
Base URL https://api.aquabotany.com/v1
bb_aquabotany) these three catalogs are a shared community feed: a read returns every contributor's rows, and creating, updating, or deleting a row requires the admin key (x-admin-key) or it returns 403. Completing a challenge is not a catalog write and needs no admin key: POST and DELETE on /challenges/{challengeId}/complete record your own progress, so every user of the product can take part in a shared challenge independently. On your own project platform (or the default platform) the catalog is private to your account: a list or by-id read returns only rows you created (another account reusing the same platform name sees nothing of yours, and an unknown or foreign id returns 404), and you may create freely but can only edit, complete, or delete a row you created. Another account's row is not addressable from yours at all, so acting on one returns 404, exactly as an unknown id does. An id you supply yourself is scoped to your account on a private platform, so two accounts can each use the same challengeId or rewardId without colliding.List your logged actions, newest first.
An action's timestamp is part of its sort key, so since and until are a real keyed range: only the matching rows are read. Use them for a date-bounded history, such as a progress chart or an export of last quarter, rather than paging backwards until you walk past the date you wanted.
| Parameter | In | Description |
|---|---|---|
platform | query | Optional. Namespace to read. Omitted means your default namespace; * reads across every platform you have. |
since | query | Optional. Inclusive lower bound: YYYY-MM-DD, or a timestamp such as 2026-03-14T09:30:00. A Z or +HH:MM suffix is accepted and converted to UTC, so new Date().toISOString() works as-is. Requires a single platform. |
until | query | Optional. Inclusive upper bound, same format. A bare date covers the whole of that day, not just midnight. Requires a single platform. |
limit | query | Optional. Rows per page, 1 to 1000. Defaults to 20. |
startKey | query | Optional. The nextKey from a previous response, to fetch the next page. |
The range cannot be combined with platform=*. In the sort key the platform segment sits between the entity and the timestamp, so a range spanning every platform would bound platform names rather than dates. Rather than return a confidently wrong answer the endpoint refuses with 400 RANGE_NEEDS_PLATFORM.
A bound is also refused when it is not a usable date, with a code naming the side that failed: 400 BAD_SINCE or 400 BAD_UNTIL for a value containing # (it would forge sort-key structure), for a malformed value, or for a date that does not exist such as 2026-02-30. 400 BAD_RANGE means since is after until. A non-integer limit is 400 BAD_LIMIT, and a startKey that is not a cursor this endpoint minted is 400 BAD_START_KEY.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/actions?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"
# A date range. Needs a single platform, not "*".
# until is inclusive: this covers all of March 31.
curl "https://api.aquabotany.com/v1/actions?platform=my_garden&since=2026-01-01&until=2026-03-31&limit=200" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)
# A date range. Needs a single platform, not "*".
# until is inclusive: this covers all of March 31.
resp = requests.get(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
params={
"platform": "my_garden",
"since": "2026-01-01",
"until": "2026-03-31",
"limit": 200,
},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// A date range. Needs a single platform, not "*".
// until is inclusive: this covers all of March 31.
const range = await fetch(
"https://api.aquabotany.com/v1/actions?platform=my_garden&since=2026-01-01&until=2026-03-31&limit=200",
{ method: "GET", headers: { "x-api-key": API_KEY } }
);// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// A date range. Needs a single platform, not "*".
// until is inclusive: this covers all of March 31.
const range = await fetch(
"https://api.aquabotany.com/v1/actions?platform=my_garden&since=2026-01-01&until=2026-03-31&limit=200",
{ method: "GET", headers: { "x-api-key": API_KEY } }
);{
"actions": [
{
"userId": "coughlin058@gmail.com",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
"timestamp": "2026-07-08T09:14:23.481932",
"recordedAt": "2026-07-08T09:14:23.481932",
"actionType": "watering",
"description": "Watered garden",
"metadata": {}
},
{
"userId": "coughlin058@gmail.com",
"actionId": "4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11",
"timestamp": "2026-07-07T18:02:11.905417",
"recordedAt": "2026-07-07T18:02:11.905417",
"actionType": "composting",
"description": "Added kitchen scraps to compost bin",
"metadata": {}
}
],
"nextKey": null
}{
"error": "since/until require a single platform, not '*'",
"code": "RANGE_NEEDS_PLATFORM"
}Log an action.
By default the server stamps the current time. Pass occurredAt to file the action under when it actually HAPPENED, which is what you want for anything the user is recording rather than doing: a receipt from Saturday belongs on Saturday. The timestamp is part of the sort key, so this is also what keeps a date range exact rather than approximate.
| Parameter | In | Description |
|---|---|---|
actionType | body | Optional string. Defaults to unknown. |
description | body | Optional string. |
metadata | body | Optional object. Free-form; stored and returned as given. |
occurredAt | body | Optional. YYYY-MM-DD, or a timestamp such as 2026-03-14T09:30:00. A Z or +HH:MM suffix is accepted and converted to UTC, so new Date().toISOString() works as-is. Absent means now. |
platform | body | Optional. Namespace to write to. * is not allowed on writes. |
Responses carry both timestamp, when the action happened, and recordedAt, when the row was written. recordedAt is written at create time and was added on 2026-09-06 with no backfill, so rows created before that date do not have it: null-check rather than assuming. They are equal unless the action was backdated, and recordedAt is what orders two actions filed against the same day.
occurredAt is key material, so it is validated strictly and returns 400 BAD_OCCURRED_AT for a value containing #, a date that does not exist such as 2026-02-30, anything before 2000-01-01, or a date more than a day in the future. A non-string actionType or description, or a non-object metadata, is 400 BAD_TYPE; passing the per-user row cap is 409 LIMIT_REACHED.
That last one allows for client clock skew while refusing a row that would sort ahead of every real action forever.
A date is write-once: PATCH does not move an action in time, so correcting one means deleting and recreating it.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "watering", "description": "Watered garden"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/actions \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "watering", "description": "Watered garden", "platform": "my_garden"}'
# Backdated: file this under the day it happened, not today
curl https://api.aquabotany.com/v1/actions \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "expense", "description": "Groceries", "occurredAt": "2026-03-14", "platform": "my_budget"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
json={'actionType': 'watering', 'description': 'Watered garden'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'actionType': 'watering', 'description': 'Watered garden', 'platform': 'my_garden'},
)
# Backdated: file this under the day it happened, not today
resp = requests.post(
"https://api.aquabotany.com/v1/actions",
headers={"x-api-key": API_KEY},
json={
"actionType": "expense",
"description": "Groceries",
"occurredAt": "2026-03-14",
"platform": "my_budget",
},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "watering", "description": "Watered garden"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "watering", "description": "Watered garden", "platform": "my_garden"})
});
// Backdated: file this under the day it happened, not today
const backdated = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
actionType: "expense",
description: "Groceries",
occurredAt: "2026-03-14",
platform: "my_budget"
})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "watering", "description": "Watered garden"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "watering", "description": "Watered garden", "platform": "my_garden"})
});
// Backdated: file this under the day it happened, not today
const backdated = await fetch("https://api.aquabotany.com/v1/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
actionType: "expense",
description: "Groceries",
occurredAt: "2026-03-14",
platform: "my_budget"
})
});{
"message": "Action logged",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02"
}{
"error": "occurredAt must not be in the future",
"code": "BAD_OCCURRED_AT"
}The caller's action progress.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions/user-progress \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/actions/user-progress?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/actions/user-progress",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/actions/user-progress",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/user-progress", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions/user-progress?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/user-progress", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions/user-progress?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"totalActions": 2,
"action_counts": {
"watering": 1,
"composting": 1
}
}Read, update, or delete an action. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"userId": "coughlin058@gmail.com",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
"timestamp": "2026-07-08T09:14:23.481932",
"recordedAt": "2026-07-08T09:14:23.481932",
"actionType": "watering",
"description": "Watered garden",
"metadata": {}
}Read, update, or delete an action.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
json={'actionType': 'watering', 'description': 'Watered the herb garden beds', 'metadata': {}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'actionType': 'watering', 'description': 'Watered the herb garden beds', 'metadata': {}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "watering", "description": "Watered the herb garden beds", "metadata": {}, "platform": "my_garden"})
});{
"userId": "coughlin058@gmail.com",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
"timestamp": "2026-07-08T09:14:23.481932",
"recordedAt": "2026-07-08T09:14:23.481932",
"actionType": "watering",
"description": "Watered the herb garden beds",
"metadata": {}
}Read, update, or delete an action.
| Parameter | In | Description |
|---|---|---|
actionType | body | Optional string. |
description | body | Optional string. |
metadata | body | Optional object. Replaced wholesale, not merged. |
Those three fields are the only updatable ones. An action's DATE is write-once: timestamp and occurredAt are ignored here, because the timestamp is part of the sort key and this route does not move a row. Correcting a date means deleting the action and creating it again. A body with none of the three updatable fields returns 400.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "pruning"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "pruning", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
json={'actionType': 'pruning'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'actionType': 'pruning', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "pruning"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "pruning", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"actionType": "pruning"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "pruning", "platform": "my_garden"})
});{
"userId": "coughlin058@gmail.com",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
"timestamp": "2026-07-08T09:14:23.481932",
"recordedAt": "2026-07-08T09:14:23.481932",
"actionType": "pruning",
"description": "Watered garden",
"metadata": {}
}Read, update, or delete an action.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted"
}Community challenges.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/challenges?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/challenges",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/challenges",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
"title": "Compost for a week",
"description": "Add to your compost bin every day for 7 days",
"status": "active",
"created": "2026-07-01T08:30:12.114256",
"lastModified": "2026-07-01T08:30:12.114256"
},
{
"challengeId": "7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22",
"title": "No-plastic watering month",
"description": "Water only with collected rainwater for 30 days",
"status": "completed",
"created": "2026-06-01T10:05:44.902317",
"lastModified": "2026-07-02T16:41:09.337815"
}
]Community challenges.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/challenges \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/challenges",
headers={"x-api-key": API_KEY},
json={'title': 'Compost for a week', 'description': 'Add to your compost bin every day for 7 days', 'status': 'active'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/challenges",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Compost for a week', 'description': 'Add to your compost bin every day for 7 days', 'status': 'active', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/challenges", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Compost for a week", "description": "Add to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"})
});{
"message": "Challenge created",
"challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15"
}Challenges that are published and that you have not completed yet. Completing one removes it from your list only.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/active \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/challenges/active?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/active",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/active",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/active", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/active?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/active", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/active?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
"title": "Compost for a week",
"description": "Add to your compost bin every day for 7 days",
"status": "active",
"created": "2026-07-01T08:30:12.114256",
"lastModified": "2026-07-01T08:30:12.114256"
}
]Challenges you have completed, newest data first. This is your own history, not a list of challenges flagged finished.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/history \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/challenges/history?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/history",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/history",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/history?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/history?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"challengeId": "7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22",
"title": "No-plastic watering month",
"description": "Water only with collected rainwater for 30 days",
"status": "completed",
"created": "2026-06-01T10:05:44.902317",
"lastModified": "2026-07-02T16:41:09.337815"
}
]Read, update, or delete a challenge. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
"title": "Compost for a week",
"description": "Add to your compost bin every day for 7 days",
"status": "active",
"created": "2026-07-01T08:30:12.114256",
"lastModified": "2026-07-01T08:30:12.114256"
}Read, update, or delete a challenge.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
json={'title': 'Compost for a week', 'description': 'Add kitchen scraps to your compost bin every day for 7 days', 'status': 'active'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Compost for a week', 'description': 'Add kitchen scraps to your compost bin every day for 7 days', 'status': 'active', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Compost for a week", "description": "Add kitchen scraps to your compost bin every day for 7 days", "status": "active", "platform": "my_garden"})
});{
"message": "Challenge updated"
}Read, update, or delete a challenge.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});(no response body)Mark a challenge complete for you. Completion is per account: finishing a shared challenge does not finish it for anyone else, and on a Bottle Blue product platform any signed-in user may complete one without the admin key. Completing twice returns 409.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete \
-X POST \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete?platform=my_garden" \
-X POST \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete", {
method: "POST",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete?platform=my_garden", {
method: "POST",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete", {
method: "POST",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete?platform=my_garden", {
method: "POST",
headers: { "x-api-key": API_KEY }
});{
"message": "Challenge marked as completed",
"challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
"completedAt": "2026-08-01T22:41:07.184213"
}Un-complete a challenge you previously finished. Removes only your completion, never anyone else's, and puts the challenge back in your /challenges/active list. Returns 204 on success, or 404 if you had not completed it.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete -X DELETE -H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete?platform=my_garden" -X DELETE -H "x-api-key: $BBL_API_KEY"import requests
resp = requests.delete(
"https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete",
headers={"x-api-key": API_KEY},
)
print(resp.status_code) # 204 undone, 404 if it was not completedconst resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
// 204 undone, 404 if it was not completedconst resp = await fetch("https://api.aquabotany.com/v1/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
// 204 undone, 404 if it was not completed(no body)Community events.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/events?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/events",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/events",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"events": [
{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
"title": "Seed swap",
"type": "community",
"description": "Bring your extra seeds and trade with fellow gardeners.",
"datetime": "2026-08-01T10:00:00",
"details": "Held at the Loring Park pavilion. Rain or shine.",
"lastModified": "2026-07-01T09:15:42.318204"
},
{
"eventId": "5b1e9f60-2c3d-4a87-b6e4-9f0d2c7a4e18",
"title": "Rain barrel workshop",
"type": "workshop",
"description": "Learn to build and install a rain barrel for garden irrigation.",
"datetime": "2026-07-18T13:30:00",
"details": "",
"lastModified": "2026-06-25T16:40:07.902113"
}
]
}Community events.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/events \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/events",
headers={"x-api-key": API_KEY},
json={'title': 'Seed swap', 'type': 'community', 'description': 'Bring your extra seeds and trade with fellow gardeners.', 'datetime': '2026-08-01T10:00:00', 'details': 'Held at the Loring Park pavilion. Rain or shine.'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/events",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Seed swap', 'type': 'community', 'description': 'Bring your extra seeds and trade with fellow gardeners.', 'datetime': '2026-08-01T10:00:00', 'details': 'Held at the Loring Park pavilion. Rain or shine.', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-01T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"})
});{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73"
}Upcoming events.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events/upcoming \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/events/upcoming?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/events/upcoming",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/events/upcoming",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/upcoming", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events/upcoming?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/upcoming", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events/upcoming?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"events": [
{
"eventId": "5b1e9f60-2c3d-4a87-b6e4-9f0d2c7a4e18",
"title": "Rain barrel workshop",
"type": "workshop",
"description": "Learn to build and install a rain barrel for garden irrigation.",
"datetime": "2026-07-18T13:30:00",
"details": "",
"lastModified": "2026-06-25T16:40:07.902113"
},
{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
"title": "Seed swap",
"type": "community",
"description": "Bring your extra seeds and trade with fellow gardeners.",
"datetime": "2026-08-01T10:00:00",
"details": "Held at the Loring Park pavilion. Rain or shine.",
"lastModified": "2026-07-01T09:15:42.318204"
}
]
}Read, update, or delete an event. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
"title": "Seed swap",
"type": "community",
"description": "Bring your extra seeds and trade with fellow gardeners.",
"datetime": "2026-08-01T10:00:00",
"details": "Held at the Loring Park pavilion. Rain or shine.",
"lastModified": "2026-07-01T09:15:42.318204"
}Read, update, or delete an event.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
json={'title': 'Seed swap (rescheduled)', 'type': 'community', 'description': 'Bring your extra seeds and trade with fellow gardeners.', 'datetime': '2026-08-08T10:00:00', 'details': 'Held at the Loring Park pavilion. Rain or shine.'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Seed swap (rescheduled)', 'type': 'community', 'description': 'Bring your extra seeds and trade with fellow gardeners.', 'datetime': '2026-08-08T10:00:00', 'details': 'Held at the Loring Park pavilion. Rain or shine.', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap (rescheduled)", "type": "community", "description": "Bring your extra seeds and trade with fellow gardeners.", "datetime": "2026-08-08T10:00:00", "details": "Held at the Loring Park pavilion. Rain or shine.", "platform": "my_garden"})
});{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
"title": "Seed swap (rescheduled)",
"type": "community",
"description": "Bring your extra seeds and trade with fellow gardeners.",
"datetime": "2026-08-08T10:00:00",
"details": "Held at the Loring Park pavilion. Rain or shine.",
"lastModified": "2026-07-08T14:22:31.594820"
}Read, update, or delete an event.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap (rescheduled)"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Seed swap (rescheduled)", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
json={'title': 'Seed swap (rescheduled)'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Seed swap (rescheduled)', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap (rescheduled)"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap (rescheduled)", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Seed swap (rescheduled)"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Seed swap (rescheduled)", "platform": "my_garden"})
});{
"eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
"title": "Seed swap (rescheduled)",
"type": "community",
"description": "Bring your extra seeds and trade with fellow gardeners.",
"datetime": "2026-08-01T10:00:00",
"details": "Held at the Loring Park pavilion. Rain or shine.",
"lastModified": "2026-07-08T14:25:03.117456"
}Read, update, or delete an event.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted"
}Available rewards.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/rewards?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/rewards",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/rewards",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/rewards?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/rewards?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
"title": "Free seed packet",
"pointsRequired": 100,
"description": "Redeem for a heirloom tomato seed packet."
},
{
"rewardId": "e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04",
"title": "10% off soil amendments",
"pointsRequired": 250,
"description": "One-time 10% discount on any soil amendment order."
}
]Available rewards.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet."}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/rewards \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet.", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/rewards",
headers={"x-api-key": API_KEY},
json={'title': 'Free seed packet', 'pointsRequired': 100, 'description': 'Redeem for a heirloom tomato seed packet.'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/rewards",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Free seed packet', 'pointsRequired': 100, 'description': 'Redeem for a heirloom tomato seed packet.', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet.", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Free seed packet", "pointsRequired": 100, "description": "Redeem for a heirloom tomato seed packet.", "platform": "my_garden"})
});{
"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61"
}The caller's reward-redemption history.
curl https://api.aquabotany.com/v1/rewards/history \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/rewards/history",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/rewards/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/rewards/history", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
"timestamp": "2026-07-08T14:31:09.284511Z"
},
{
"rewardId": "e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04",
"timestamp": "2026-06-30T11:02:47.910335Z"
}
]The caller's current points balance.
curl https://api.aquabotany.com/v1/rewards/points \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/rewards/points",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/rewards/points", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/rewards/points", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"points": 175
}Redeem points for a reward.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards/redeem \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/rewards/redeem \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/rewards/redeem",
headers={"x-api-key": API_KEY},
json={'rewardId': 'c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/rewards/redeem",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'rewardId': 'c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/redeem", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/redeem", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/redeem", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/redeem", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", "platform": "my_garden"})
});{
"message": "Reward redeemed successfully",
"remainingPoints": 125
}Read, update, or delete a reward. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61 \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
"title": "Free seed packet",
"pointsRequired": 100,
"description": "Redeem for a heirloom tomato seed packet.",
"created": "2026-06-15T08:20:33.104928Z",
"lastModified": "2026-06-15T08:20:33.104928Z"
}Read, update, or delete a reward.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pointsRequired": 120}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pointsRequired": 120, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
json={'pointsRequired': 120},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'pointsRequired': 120, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"pointsRequired": 120})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"pointsRequired": 120, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"pointsRequired": 120})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"pointsRequired": 120, "platform": "my_garden"})
});{
"rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
"title": "Free seed packet",
"pointsRequired": 120,
"description": "Redeem for a heirloom tomato seed packet.",
"created": "2026-06-15T08:20:33.104928Z",
"lastModified": "2026-07-08T14:36:52.771204Z"
}Read, update, or delete a reward.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});(no response body)Notifications
Base URL https://api.aquabotany.com/v1
Create a notification for the authenticated account.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Time to water", "body": "Your basil needs water"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/notifications \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Time to water", "body": "Your basil needs water", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/notifications",
headers={"x-api-key": API_KEY},
json={'title': 'Time to water', 'body': 'Your basil needs water'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/notifications",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'title': 'Time to water', 'body': 'Your basil needs water', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Time to water", "body": "Your basil needs water"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Time to water", "body": "Your basil needs water", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Time to water", "body": "Your basil needs water"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Time to water", "body": "Your basil needs water", "platform": "my_garden"})
});{
"message": "Notification created",
"notifId": "9f3c1a2b4d5e6f708192a3b4c5d6e7f8"
}Read or update your notifications (scoped to the authenticated caller).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications/you@example.com \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/notifications/you@example.com?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/notifications/you@example.com",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/notifications/you@example.com",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"notifId": "9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
"title": "Time to water",
"body": "Your basil needs water",
"status": "unread",
"createdAt": "2026-07-08T14:12:31.482913"
},
{
"notifId": "3d0a7b8c9e1f2a3b4c5d6e7f80912a3b",
"title": "Fertilize reminder",
"body": "Feed the tomatoes this week",
"status": "read",
"createdAt": "2026-07-07T09:03:55.120044"
}
]Read or update your notifications (scoped to the authenticated caller).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications/you@example.com \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "read"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/notifications/you@example.com \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "read", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/notifications/you@example.com",
headers={"x-api-key": API_KEY},
json={'status': 'read'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/notifications/you@example.com",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'status': 'read', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "read"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "read", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "read"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "read", "platform": "my_garden"})
});{
"message": "Unsupported route"
}Mark all of your notifications read.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications/you@example.com/read-all \
-X POST \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/notifications/you@example.com/read-all?platform=my_garden" \
-X POST \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/notifications/you@example.com/read-all",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/notifications/you@example.com/read-all",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/read-all", {
method: "POST",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/read-all?platform=my_garden", {
method: "POST",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/read-all", {
method: "POST",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/read-all?platform=my_garden", {
method: "POST",
headers: { "x-api-key": API_KEY }
});{
"message": "Marked 2 notifications as read."
}Update or delete one of your notifications.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "read"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8 \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "read", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
json={'status': 'read'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'status': 'read', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "read"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "read", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "read"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "read", "platform": "my_garden"})
});{
"notifId": "9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
"title": "Time to water",
"body": "Your basil needs water",
"status": "read",
"createdAt": "2026-07-08T14:12:31.482913"
}Update or delete one of your notifications.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});(no response body)Content & settings
Base URL https://api.aquabotany.com/v1
Educational gardening resources.
Results are scoped to the platform you ask for, and only that platform's library is returned. A platform whose library is itself part of what that product sells is not served here and returns 403 PAID_CATALOG; the wildcard * is not accepted and returns 400 WILDCARD_NOT_ALLOWED. Omitting platform reads the default namespace, which holds no shared library.
curl https://api.aquabotany.com/v1/learning-resources \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/learning-resources",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/learning-resources", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/learning-resources", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"type": "internal",
"title": "Getting Started with Hydroponics",
"description": "A beginner's guide to setting up your first hydroponic system.",
"author": "Aqua Botany Team",
"date": "2026-06-15T10:30:00",
"thumbnail": "https://cdn.aquabotany.com/articles/hydroponics-101.jpg",
"content": "Hydroponics is the practice of growing plants without soil...",
"link": null
},
{
"id": "f6e5d4c3-b2a1-4d9c-8b7a-6f5e4d3c2b1a",
"type": "external",
"title": "USDA Plant Hardiness Zone Map",
"description": "Find your growing zone before planning your garden.",
"author": "USDA",
"date": "2026-05-02T08:00:00",
"thumbnail": null,
"content": null,
"link": "https://planthardiness.ars.usda.gov/"
}
]
// 403 - this platform's library is part of what the product sells
{
"error": "this platform's catalog is a paid product and is not served here",
"code": "PAID_CATALOG"
}
// 400 - one platform at a time
{
"error": "this endpoint reads one platform at a time; '*' is not accepted",
"code": "WILDCARD_NOT_ALLOWED"
}Read all account settings.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/settings \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/settings?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/settings",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/settings",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/settings?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/settings?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"value": {
"system": "metric"
},
"lastModified": "2026-07-08T14:15:09.271836",
"settingName": "units"
},
{
"value": {
"email": true,
"push": false
},
"lastModified": "2026-07-06T18:40:12.503311",
"settingName": "notifications"
}
]Create or update a setting.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/settings \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"settingName": "units", "value": {"system": "metric"}}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/settings \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"settingName": "units", "value": {"system": "metric"}, "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/settings",
headers={"x-api-key": API_KEY},
json={'settingName': 'units', 'value': {'system': 'metric'}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/settings",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'settingName': 'units', 'value': {'system': 'metric'}, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"settingName": "units", "value": {"system": "metric"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/settings", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"settingName": "units", "value": {"system": "metric"}, "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"settingName": "units", "value": {"system": "metric"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/settings", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"settingName": "units", "value": {"system": "metric"}, "platform": "my_garden"})
});{
"message": "Setting saved"
}Read or delete one setting. Supports ?expand=links to inline this record's links in the same call - see Link System.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/settings/units \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/settings/units?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/settings/units",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/settings/units",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings/units", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/settings/units?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings/units", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/settings/units?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"value": {
"system": "metric"
},
"lastModified": "2026-07-08T14:15:09.271836",
"settingName": "units"
}Read or delete one setting.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/settings/units \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/settings/units?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/settings/units",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/settings/units",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings/units", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/settings/units?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/settings/units", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/settings/units?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});(no response body)Aqua Botany
Base URL https://api.aquabotany.com/v1
The caller's dashboard summary.
curl https://api.aquabotany.com/v1/dashboard \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/v1/dashboard",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/v1/dashboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.aquabotany.com/v1/dashboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"serial": "12345",
"sensordata": {
"temp": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 23.1
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 22.8
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 22.4
}
]
},
"soilmoisture": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 46.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 47.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 48.0
}
]
},
"sunlight": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 315.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 340.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 298.0
}
]
}
}
},
{
"serial": "54321",
"sensordata": {
"temp": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 21.7
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 21.5
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 21.2
}
]
},
"soilmoisture": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 51.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 52.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 52.0
}
]
},
"sunlight": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 402.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 415.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 388.0
}
]
}
}
},
{
"serial": "00000",
"sensordata": {
"temp": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 24.3
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 24.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 23.9
}
]
},
"soilmoisture": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 39.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 41.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 42.0
}
]
},
"sunlight": {
"readings": [
{
"timestamp": "2026-07-08T14:00:00Z",
"value": 512.0
},
{
"timestamp": "2026-07-08T13:00:00Z",
"value": 530.0
},
{
"timestamp": "2026-07-08T12:00:00Z",
"value": 505.0
}
]
}
}
}
]Work orders
Base URL https://api.aquabotany.com/v1
orgId that reads or stores the work order in a shared organization's partition instead of your own (see Organizations). Omitting orgId behaves exactly as it did before this was added, so existing integrations need no change. Send it in the query string on the GET routes and in the JSON body on POST, PUT and PATCH; DELETE accepts either. It is a string of at most 128 characters made up of letters, digits and _.:- only, and anything else returns 400 INVALID_ORG_ID. Membership is checked fail closed: if it cannot be verified the call returns 403 NOT_ORG_MEMBER rather than quietly falling back to your own work orders. The member role covers list, read, create and update, while DELETE also requires admin, since removing shared inventory is more destructive than editing it. Work orders created under an organization count against that organization's shared cap of 2000 rows, which is tracked separately from the 500 row per account cap.List the caller's work orders, or an organization's shared work orders when orgId is given.
Rows come back with normalized lowercase keys: id, platform, title, department, status, priority, assigned_to, notes, created_at, updated_at and created_by. GET /workorder/{workorderId} returns the same shape, so a client needs only one mapper.
| Parameter | In | Description |
|---|---|---|
orgId | query | Optional. List an organization's shared work orders instead of your own (see Organizations). Any member role can list. |
platform | query | Optional. Namespace to read. Omitted means your default namespace; * reads across every platform you have. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/workorder?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"
# Scoped to an organization (shared work orders)
curl "https://api.aquabotany.com/v1/workorder?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)
# Scoped to an organization (shared work orders)
resp = requests.get(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
params={"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/workorder?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// Scoped to an organization (shared work orders)
const shared = await fetch("https://api.aquabotany.com/v1/workorder?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be "*" (read across every platform)
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/workorder?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
// Scoped to an organization (shared work orders)
const shared = await fetch("https://api.aquabotany.com/v1/workorder?orgId=3f1c9e2a7d614b839e5a3f8b1c6d2e07", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
"platform": "default",
"title": "Replace irrigation pump",
"department": "Maintenance",
"status": "New",
"priority": "High",
"assigned_to": null,
"created_at": "2026-07-08T14:18:02.331674+00:00",
"updated_at": "2026-07-08T14:18:02.331674+00:00"
},
{
"id": "0a9b8c7d-6e5f-4d3c-2b1a-9f8e7d6c5b4a",
"platform": "default",
"title": "Calibrate pH sensors",
"department": "Operations",
"status": "In Progress",
"priority": "Normal",
"assigned_to": "tech@example.com",
"created_at": "2026-07-05T09:22:47.905112+00:00",
"updated_at": "2026-07-07T16:03:11.284559+00:00"
}
]{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}Create a work order. New orders start with status New.
Returns {"status": "created", "workorder_id": "<uuid>"}. Missing title or department returns 400.
| Parameter | In | Description |
|---|---|---|
title | body | required Short description of the work. |
department | body | required Owning department, for example Maintenance. |
assigned_to | body | Optional. Email address of the person responsible. Defaults to unassigned. |
priority | body | Optional. Defaults to Normal. |
notes | body | Optional free text. |
orgId | body | Optional. Create the work order in an organization's shared partition instead of your own (see Organizations). Any member role can create. |
platform | body | Optional. Namespace to write to. Defaults to default; * is not allowed on writes. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3."}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/workorder \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3.", "platform": "my_garden"}'
# Stored in an organization's shared partition (orgId goes in the body on writes)
curl https://api.aquabotany.com/v1/workorder \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Replace irrigation pump", "department": "Maintenance", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
json={"title": "Replace irrigation pump", "department": "Maintenance",
"priority": "High", "notes": "Pump failed on section 3."},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={"title": "Replace irrigation pump", "department": "Maintenance",
"priority": "High", "notes": "Pump failed on section 3.", "platform": "my_garden"},
)
# Stored in an organization's shared partition (orgId goes in the body on writes)
resp = requests.post(
"https://api.aquabotany.com/v1/workorder",
headers={"x-api-key": API_KEY},
json={"title": "Replace irrigation pump", "department": "Maintenance",
"orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3.", "platform": "my_garden"})
});
// Stored in an organization's shared partition (orgId goes in the body on writes)
const shared = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3."})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "priority": "High", "notes": "Pump failed on section 3.", "platform": "my_garden"})
});
// Stored in an organization's shared partition (orgId goes in the body on writes)
const shared = await fetch("https://api.aquabotany.com/v1/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"title": "Replace irrigation pump", "department": "Maintenance", "orgId": "3f1c9e2a7d614b839e5a3f8b1c6d2e07"})
});{
"status": "created",
"workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}{
"error": "Item limit reached (2000); delete some before adding more",
"code": "LIMIT_REACHED"
}Get a single work order. Supports ?expand=links to inline this record's links in the same call - see Link System. Returns 404 if there is no such work order, or if it belongs to another account and no orgId was given.
This route returns the same normalized shape as GET /workorder: id, platform, title, department, status, priority, assigned_to, notes, created_at, updated_at and created_by. Until 2026-09-05 it returned the raw stored attribute names instead (WorkOrderId, Title and so on), so one work order had two spellings depending on which route you called. Both reads now go through a single projection and cannot drift apart again.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
orgId | query | Optional. Read an organization's shared work order instead of your own (see Organizations). Any member role can read. |
expand | query | Optional. Pass expand=links to inline this work order's relationships from Links under a links key, both directions, instead of making a second call. Capped at 50 rows per direction; a truncated flag says to page through /links instead. |
platform | query | Optional. Namespace the work order lives in. Omitted means your default namespace. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the Aqua Botany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform must match where the item lives: a project name
// or the Aqua Botany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"id": "8c71b74a-89bc-4a74-bdfe-8ba772fab124",
"platform": "default",
"title": "Replace Pump",
"department": "Maintenance",
"status": "In Progress",
"priority": "High",
"assigned_to": "alex@acme.com",
"notes": "Waiting for part",
"created_at": "2026-09-05T12:00:00Z",
"updated_at": "2026-09-05T14:30:00Z",
"created_by": "owner@acme.com"
}{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}Update a work order.
Supply at least one of title, department, assigned_to, priority, status or notes; a request with no fields returns 400. Returns {"status": "updated", "workorder_id": "<uuid>"}.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
title | body | Optional. Short description of the work. |
department | body | Optional. Owning department. |
assigned_to | body | Optional. Email address of the person responsible. |
priority | body | Optional. |
status | body | Optional, for example In Progress. |
notes | body | Optional free text. |
orgId | body | Optional. Update an organization's shared work order instead of your own (see Organizations). Any member role can update. |
platform | body | Optional. Namespace to write to. Defaults to default; * is not allowed on writes. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "In Progress", "assigned_to": "tech@example.com"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.put(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
json={'status': 'In Progress', 'assigned_to': 'tech@example.com'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.put(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'status': 'In Progress', 'assigned_to': 'tech@example.com', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"})
});{
"status": "updated",
"workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}Update a work order. Identical to PUT; both apply a partial update.
Supply at least one of title, department, assigned_to, priority, status or notes; a request with no fields returns 400. Returns {"status": "updated", "workorder_id": "<uuid>"}.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
title | body | Optional. Short description of the work. |
department | body | Optional. Owning department. |
assigned_to | body | Optional. Email address of the person responsible. |
priority | body | Optional. |
status | body | Optional, for example In Progress. |
notes | body | Optional free text. |
orgId | body | Optional. Update an organization's shared work order instead of your own (see Organizations). Any member role can update. |
platform | body | Optional. Namespace to write to. Defaults to default; * is not allowed on writes. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "In Progress", "assigned_to": "tech@example.com"}'
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X PATCH \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.patch(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
json={'status': 'In Progress', 'assigned_to': 'tech@example.com'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'status': 'In Progress', 'assigned_to': 'tech@example.com', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"})
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"status": "In Progress", "assigned_to": "tech@example.com", "platform": "my_garden"})
});{
"status": "updated",
"workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}Delete a work order.
Returns {"status": "deleted", "workorder_id": "<uuid>"}. Scoped to the authenticated user unless orgId is given; another account's ids return 404. This is the one route in the group that is not open to every member: deleting shared inventory requires the admin role or higher, and a plain member gets 403 INSUFFICIENT_ORG_ROLE.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
orgId | query or body | Optional. Delete an organization's shared work order instead of your own (see Organizations). Accepted in the query string or the JSON body. Requires the admin role or higher. |
platform | query | Optional. Namespace the work order lives in. Omitted means your default namespace. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden" \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.delete(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.delete(
"https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the Aqua Botany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the Aqua Botany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/v1/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"status": "deleted",
"workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}{
"error": "invalid orgId (letters, digits, and _.:- only)",
"code": "INVALID_ORG_ID"
}{
"error": "You are not a member of this organization",
"code": "NOT_ORG_MEMBER"
}{
"error": "This action requires the 'admin' role or higher",
"code": "INSUFFICIENT_ORG_ROLE"
}PocketQuant
Base URL https://api.pocketquant.io
The caller's dashboard summary.
curl https://api.pocketquant.io/dashboard \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.pocketquant.io/dashboard",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/dashboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.pocketquant.io/dashboard", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"message": "Dashboard"
}Search stocks and symbols.
curl https://api.pocketquant.io/search \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"searchTerm": "Renewable"}'import requests
resp = requests.post(
"https://api.pocketquant.io/search",
headers={"x-api-key": API_KEY},
json={'searchTerm': 'Renewable'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/search", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"searchTerm": "Renewable"})
});
const data = await resp.json();const resp = await fetch("https://api.pocketquant.io/search", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"searchTerm": "Renewable"})
});
const data = await resp.json();[
{
"Ticker": "BEP",
"Date": "2026-07-08",
"Company Name": "Brookfield Renewable Partners L.P.",
"About": "Brookfield Renewable Partners L.P. owns and operates one of the world's largest publicly traded platforms for renewable power, with hydroelectric, wind, solar, and storage facilities in North America, South America, Europe, and Asia.",
"CEO": "Connor Teskey",
"Country": "Canada",
"IPO Year": "2011",
"Volume": "614,532",
"Sector": "Utilities",
"Industry": "Renewable Utilities",
"Ticker Verbose": "BEP:NYSE",
"Employees": "3,400",
"Founded": "1999",
"Market cap": "16.42B USD",
"Primary exchange": "NYSE",
"Website": "https://bep.brookfield.com",
"Headquarters": "Toronto, Ontario, Canada"
}
]Statistics for a ticker.
| Parameter | In | Description |
|---|---|---|
ticker | path | required Stock symbol. |
curl https://api.pocketquant.io/stats/AAPL \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.pocketquant.io/stats/AAPL",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/stats/AAPL", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.pocketquant.io/stats/AAPL", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"ticker": "AAPL",
"stats": {
"marketCap": 3182456750080,
"trailingPE": 33.12,
"dividendYield": 0.49,
"averageVolume": 54832100,
"dayHigh": 214.65,
"dayLow": 211.02,
"open": 211.85,
"volume": 48213450,
"fiftyTwoWeekHigh": 260.1,
"fiftyTwoWeekLow": 169.21
}
}Quote and data for a ticker.
| Parameter | In | Description |
|---|---|---|
ticker | path | required Stock symbol, e.g. AAPL. |
curl https://api.pocketquant.io/stocks/AAPL \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.pocketquant.io/stocks/AAPL",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/stocks/AAPL", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.pocketquant.io/stocks/AAPL", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"ticker": "AAPL",
"prices": [
207.82,
210.01,
213.55
],
"dates": [
"2026-07-02",
"2026-07-06",
"2026-07-07"
],
"info": {
"symbol": "AAPL",
"exchange": "NasdaqGS",
"currency": "USD",
"type": "EQUITY",
"name": "Apple Inc",
"sector": "TECHNOLOGY",
"industry": "Consumer Electronics",
"description": "Apple Inc. designs, manufactures, and markets smartphones, personal computers, tablets, wearables, and accessories worldwide."
},
"stats": {
"market_cap": 3182456750080,
"pe_ratio": 33.12,
"dividend_yield": 0.0049,
"fifty_two_week_high": 260.1,
"fifty_two_week_low": 169.21,
"day_high": 214.65,
"day_low": 211.02,
"volume": 48213450,
"previous_close": 211.5,
"latest_price": 213.55
},
"related": []
}Other
Base URL https://bottlebluellc.com
Liveness probe. Returns an ok status; requires no authentication.
curl https://bottlebluellc.com/health \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://bottlebluellc.com/health",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://bottlebluellc.com/health", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://bottlebluellc.com/health", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"status": "ok",
"message": "Endpoint is live"
}Liveness probe. Returns an ok status; requires no authentication.
curl https://api.pocketquant.io/health \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.pocketquant.io/health",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/health", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();const resp = await fetch("https://api.pocketquant.io/health", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"status": "ok",
"service": "PocketQuant API",
"message": "API is healthy and responding.",
"timestamp": "2026-07-08T14:32:07.412896+00:00"
}