Bottle API Reference
One authenticated REST platform behind several products: financial and economic market data, gardening and environment data, and general-purpose developer utilities.
Every request is JSON over HTTPS and authenticated with an API key sent in the
x-api-key header. There are two primary hosts:
| Host | Serves |
|---|---|
https://api.bottlebluellc.com | Platform: market and government data, utilities, accounts, billing, authentication. |
https://api.aquabotany.com | AquaBotany: weather, environment, plants, tasks, community, and gamification. |
Versioned URLs. Every endpoint is reachable at both the
unversioned path and a /v1 prefix - for example
https://api.aquabotany.com/tasks and
https://api.aquabotany.com/v1/tasks are equivalent (the gateway
strips the /v1 prefix before routing). The /v1 form is
the recommended, stable-looking way to call the API; the unversioned paths keep
working. Future versions (/v2, ...) may be introduced later.
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 a long 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. A paid plan (Hobby, Pro, or Enterprise) is what your key can reach: the data endpoints are metered and require one, so activate a plan before your first data call.
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 like AWS Secrets Manager or Doppler.
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/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/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/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
is a real tier but authorizes no endpoint, so a Free-only key (or a key with no
subscription) is rejected with
403. The examples below read the key
from a BBL_API_KEY environment variable; never hard-code it.
One exception: the endpoints that mint, extend, or revoke API keys (/generate-key, /extend-key, /revoke-key) do not accept an API key at all. They require a Cognito bearer token from /auth/signin, sent as Authorization: Bearer <access_token>, so that a leaked key cannot be used to issue itself a replacement and outlive its own revocation. Reading your keys with /get-keys still works with an API key.
export BBL_API_KEY="your_key_here"
curl https://api.bottlebluellc.com/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/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/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 custom. Codes: BURST_LIMIT_EXCEEDED / DAILY_LIMIT_EXCEEDED. |
| 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. |
Upstream errors (502, 503, 504)
Several endpoints wrap third-party data providers: air quality comes from AirNow, weather from OpenWeather, species observations from iNaturalist, and so on. When one of those providers fails, the response says so explicitly rather than reporting a generic server error, so you can tell "retry in a moment" apart from "this request will never work".
These responses carry two extra fields alongside the usual envelope: a stable code, and an upstream naming the provider that failed.
{
"error": "The air-quality provider did not respond in time.",
"code": "UPSTREAM_TIMEOUT",
"upstream": "airnow"
}These are safe to retry, ideally with backoff, because the failure is on the provider's side and is usually brief. A 500 is not: it means our own code failed, it carries no code or upstream field, and retrying the same request will normally fail the same way. Quote the requestId instead.
The prose in error may be reworded over time; treat code as the value to branch on.
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: this is the same field, and the same value, as the
platform sent to the /auth/* sign-in endpoints. Signing
in with 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. Two different accounts can safely reuse the same project name. |
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. |
100000000000001(the numeric id behind a slug) | The lower-level product id a slug resolves to; a slug and its id name the same namespace. The numeric id space is reserved: it must be a registered, active product or the request is rejected 404 UNKNOWN_PLATFORM. Product namespaces require a paid API plan (Hobby, Pro, or Enterprise). 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 every platform you have data in. Reads only; writing to * is rejected 400 WILDCARD_WRITE. |
You cannot name a project default or another reserved word
(platform, all, none); those return
400 RESERVED_PLATFORM.
A single item addressed by id lives in exactly one platform, so fetching it
under the wrong platform returns 404.
Segmentation is enforced by the stored key, not by a filter.
Examples
# Write into a project namespace (platform in the body)
curl https://api.bottlebluellc.com/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/notes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"
# List across every platform you own
curl "https://api.bottlebluellc.com/notes?platform=*" \
-H "x-api-key: $BBL_API_KEY"import os, requests
API_KEY = os.environ["BBL_API_KEY"]
BASE = "https://api.bottlebluellc.com"
# 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";
// 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/points,POST /rewards/points/add, andGET /rewards/historyignoreplatform. The same goes forGET /images(the presigned upload URL); the platform is recorded when the upload is registered withPOST /images.
Linking records (the /links endpoint)
Every other building block stores content. /links stores relationships: a small record saying "A relates to B", where A and B are any two records in your account. That is what lets an event own its tasks, a note reference a work order, or a reminder belong to a project.
A reference is an address
Both ends of a link are written as a ref: one string that names any record in the system.
bb:task:default:abc123| Piece | Meaning |
|---|---|
bb | Fixed prefix marking a Bottle Blue reference. |
task | Which building block the record lives in. |
default | Which platform namespace, the same value you send as platform. See Data platforms. |
abc123 | The id that endpoint already handed you (event_id, task_id, note_id, and so on). |
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
A worked example
Build an event, give it a task, then ask the question that was not possible before: what belongs to what.
# 1. Create the records exactly as you do today
curl https://api.aquabotany.com/events \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Spring planting day", "event_date": "2026-09-01"}'
# -> {"event_id": "xyz789"}
curl https://api.aquabotany.com/tasks \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Buy mulch"}'
# -> {"task_id": "abc123"}
# 2. Connect them. The ids above become refs.
curl https://api.bottlebluellc.com/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"}'
# 3. What does the event own?
curl "https://api.bottlebluellc.com/links?from=bb:event:default:xyz789" \
-H "x-api-key: $BBL_API_KEY"
# 4. What is this task part of? (the reverse question)
curl "https://api.bottlebluellc.com/links?to=bb:task:default:abc123" \
-H "x-api-key: $BBL_API_KEY"import requests
AQ = "https://api.aquabotany.com"
BB = "https://api.bottlebluellc.com"
H = {"x-api-key": API_KEY}
# 1. Create the records exactly as you do today
event_id = requests.post(f"{AQ}/events", headers=H, json={
"title": "Spring planting day", "event_date": "2026-09-01"}).json()["event_id"]
task_id = requests.post(f"{AQ}/tasks", headers=H, json={
"title": "Buy mulch"}).json()["task_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",
})
# 3. What does the event own?
owned = requests.get(f"{BB}/links", headers=H,
params={"from": f"bb:event:default:{event_id}"}).json()
# 4. What is this task part of? (the reverse question)
parents = requests.get(f"{BB}/links", headers=H,
params={"to": f"bb:task:default:{task_id}"}).json()const AQ = "https://api.aquabotany.com";
const BB = "https://api.bottlebluellc.com";
const H = { "x-api-key": API_KEY, "Content-Type": "application/json" };
// 1. Create the records exactly as you do today
const { event_id } = await (await fetch(`${AQ}/events`, {
method: "POST", headers: H,
body: JSON.stringify({ title: "Spring planting day", event_date: "2026-09-01" })
})).json();
const { task_id } = await (await fetch(`${AQ}/tasks`, {
method: "POST", headers: H,
body: JSON.stringify({ title: "Buy mulch" })
})).json();
// 2. Connect them. The ids above become refs.
await fetch(`${BB}/links`, {
method: "POST", headers: H,
body: JSON.stringify({
from: `bb:event:default:${event_id}`,
to: `bb:task:default:${task_id}`,
type: "has_task"
})
});
// 3. What does the event own?
const owned = await (await fetch(
`${BB}/links?from=bb:event:default:${event_id}`, { headers: H })).json();
// 4. What is this task part of? (the reverse question)
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. A ref is a hint, not a guarantee. 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.
Authentication
Base URL https://api.bottlebluellc.com
Start a password reset (sends a reset email).
curl https://api.bottlebluellc.com/auth/forgot \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/forgot",
headers={"x-api-key": API_KEY},
json={'username': 'you@example.com', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/forgot", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"username": "you@example.com", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"message": "Password reset code sent successfully"
}Exchange a refresh token for fresh access tokens.
curl https://api.bottlebluellc.com/auth/refresh \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJjdHkiOiJ...", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/refresh",
headers={"x-api-key": API_KEY},
json={'refresh_token': 'eyJjdHkiOiJ...', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/refresh", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"refresh_token": "eyJjdHkiOiJ...", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"access_token": "eyJraWQiOiJ...",
"id_token": "eyJraWQiOiJ..."
}Create a new user account.
curl https://api.bottlebluellc.com/auth/register \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "email": "you@example.com", "password": "Str0ng!Passw0rd", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/register",
headers={"x-api-key": API_KEY},
json={'username': 'you@example.com', 'email': 'you@example.com', 'password': 'Str0ng!Passw0rd', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/register", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"username": "you@example.com", "email": "you@example.com", "password": "Str0ng!Passw0rd", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"message": "User registered successfully"
}Complete a password reset with the emailed code.
curl https://api.bottlebluellc.com/auth/reset \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "code": "123456", "new_password": "Str0ng!Passw0rd", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/reset",
headers={"x-api-key": API_KEY},
json={'username': 'you@example.com', 'code': '123456', 'new_password': 'Str0ng!Passw0rd', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/reset", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"username": "you@example.com", "code": "123456", "new_password": "Str0ng!Passw0rd", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"message": "Password has been reset successfully"
}Authenticate with email + password and receive session tokens.
curl https://api.bottlebluellc.com/auth/signin \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "password": "...", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/signin",
headers={"x-api-key": API_KEY},
json={'username': 'you@example.com', 'password': '...', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/signin", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"username": "you@example.com", "password": "...", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"access_token": "eyJraWQiOiJxNFd4MmppcURSQXRZVk5RV1F1VmhBK3M4S0hvSHNpVWFGREFFY3dmeVpZPSIsImFsZyI6IlJTMjU2In0...",
"id_token": "eyJraWQiOiI2RkRGV2FvMnIyY293aUlxaDlVaVZ6NVg3bCtVRkl6OVU0RVVxNDBZdUU0PSIsImFsZyI6IlJTMjU2In0...",
"refresh_token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ...",
"data_platform": "bb_aquabotany",
"client": "web",
"token_type": "Bearer",
"expires_in": 3600
}Complete a two-step / MFA sign-in.
curl https://api.bottlebluellc.com/auth/signin-verify \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"session_id": "b2c3d4e5-6789-01bc-def0-234567890abc"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/signin-verify",
headers={"x-api-key": API_KEY},
json={'session_id': 'b2c3d4e5-6789-01bc-def0-234567890abc'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/signin-verify", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"session_id": "b2c3d4e5-6789-01bc-def0-234567890abc"})
});
const data = await resp.json();{
"error": "Session verification is currently disabled",
"code": "FEATURE_DISABLED"
}Invalidate the current session.
curl https://api.bottlebluellc.com/auth/signout \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"access_token": "eyJraWQiOiJ...", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/signout",
headers={"x-api-key": API_KEY},
json={'access_token': 'eyJraWQiOiJ...', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/signout", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"access_token": "eyJraWQiOiJ...", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"message": "Successfully signed out"
}Check the current session / token status.
curl https://api.bottlebluellc.com/auth/status \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"access_token": "eyJraWQiOiJ...", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/status",
headers={"x-api-key": API_KEY},
json={'access_token': 'eyJraWQiOiJ...', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/status", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"access_token": "eyJraWQiOiJ...", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"valid": true,
"username": "you@example.com",
"attributes": [
{
"Name": "sub",
"Value": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "email",
"Value": "you@example.com"
}
]
}Verify a new account's email address.
curl https://api.bottlebluellc.com/auth/verify \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "code": "123456", "platform": "bb_bottleblue", "client": "web"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/auth/verify",
headers={"x-api-key": API_KEY},
json={'username': 'you@example.com', 'code': '123456', 'platform': 'bb_bottleblue', 'client': 'web'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/auth/verify", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"username": "you@example.com", "code": "123456", "platform": "bb_bottleblue", "client": "web"})
});
const data = await resp.json();{
"message": "Email verified successfully"
}API keys
Base URL https://api.bottlebluellc.com
Extend an API key's expiry.
Requires a signed-in session. This endpoint accepts a Cognito bearer token only, not an x-api-key. An API key presented here is rejected with 403 AUTH_COGNITO_REQUIRED. Key management happens through the account app over username and password, so that a leaked key cannot be used to mint, extend, or revoke keys and survive its own revocation. Read-only endpoints such as /get-keys still accept an API key.
curl https://api.bottlebluellc.com/extend-key \
-X POST \
-H "Authorization: Bearer $BBL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_key": "3f9d2c1e-8a4b-4f6e-9c2d-7b1a5e8f0d34", "days_to_extend": 30}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/extend-key",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={'api_key': '3f9d2c1e-8a4b-4f6e-9c2d-7b1a5e8f0d34', 'days_to_extend': 30},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/extend-key", {
method: "POST",
headers: { "Authorization": `Bearer ${ACCESS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({"api_key": "3f9d2c1e-8a4b-4f6e-9c2d-7b1a5e8f0d34", "days_to_extend": 30})
});
const data = await resp.json();{
"expires_at": 1786113000
}Issue a new API key for the account.
Requires a signed-in session. This endpoint accepts a Cognito bearer token only, not an x-api-key. An API key presented here is rejected with 403 AUTH_COGNITO_REQUIRED. Key management happens through the account app over username and password, so that a leaked key cannot be used to mint, extend, or revoke keys and survive its own revocation. Read-only endpoints such as /get-keys still accept an API key.
curl https://api.bottlebluellc.com/generate-key \
-X POST \
-H "Authorization: Bearer $BBL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/generate-key",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/generate-key", {
method: "POST",
headers: { "Authorization": `Bearer ${ACCESS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({})
});
const data = await resp.json();{
"api_key": "3f9d2c1e-8a4b-4f6e-9c2d-7b1a5e8f0d34",
"expires_at": 1786113000
}List the account's API keys.
curl https://api.bottlebluellc.com/get-keys \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/get-keys",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/get-keys", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"api_key_masked": "3f9d2c1e…0d34",
"key_id": "a1c4e7f0b3d6a9c2",
"email": "you@example.com",
"api_key_name": "default",
"key_status": "active",
"role": "admin",
"multi_key": false,
"auto_renew": false,
"created_at": "2026-07-08T14:30:00.123456",
"expires_at": 1786113000,
"group_id": "1"
},
{
"api_key_masked": "9b7e4a20…8a51",
"key_id": "f2b5d8c1e4a7f0b3",
"email": "you@example.com",
"api_key_name": "default",
"key_status": "revoked",
"role": "admin",
"multi_key": false,
"auto_renew": false,
"created_at": "2026-05-20T09:30:00.654321",
"expires_at": 1781861400,
"group_id": "1"
}
]Atomically revoke a key and issue a replacement.
curl https://api.bottlebluellc.com/revoke-and-generate-key \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "old_api_key": "9b7e4a20-5c3d-4e8f-a1b6-2d9c0f7e8a51"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/revoke-and-generate-key",
headers={"x-api-key": API_KEY},
json={'email': 'you@example.com', 'old_api_key': '9b7e4a20-5c3d-4e8f-a1b6-2d9c0f7e8a51'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/revoke-and-generate-key", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"email": "you@example.com", "old_api_key": "9b7e4a20-5c3d-4e8f-a1b6-2d9c0f7e8a51"})
});
const data = await resp.json();{
"error": "Revoke and generate API key is currently disabled",
"code": "FEATURE_DISABLED"
}Revoke an API key.
Requires a signed-in session. This endpoint accepts a Cognito bearer token only, not an x-api-key. An API key presented here is rejected with 403 AUTH_COGNITO_REQUIRED. Key management happens through the account app over username and password, so that a leaked key cannot be used to mint, extend, or revoke keys and survive its own revocation. Read-only endpoints such as /get-keys still accept an API key.
curl https://api.bottlebluellc.com/revoke-key \
-X POST \
-H "Authorization: Bearer $BBL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_key": "KEY"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/revoke-key",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={'api_key': 'KEY'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/revoke-key", {
method: "POST",
headers: { "Authorization": `Bearer ${ACCESS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({"api_key": "KEY"})
});
const data = await resp.json();{
"message": "API key revoked successfully",
"updatedAttributes": {
"key_status": "revoked"
}
}Validate an API key (auth infrastructure).
curl https://api.bottlebluellc.com/validate-key \
-X POST \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.post(
"https://api.bottlebluellc.com/validate-key",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/validate-key", {
method: "POST",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"error": "API access is currently disabled",
"code": "FEATURE_DISABLED"
}Subscriptions & billing
Base URL https://api.bottlebluellc.com
Start a Stripe Checkout session for a paid plan.
curl https://api.bottlebluellc.com/create-checkout-session \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"price": "price_1TsnweK6xZmbDQn2mpmU6Hkm", "success_url": "https://example.com/billing/success", "cancel_url": "https://example.com/billing/cancel"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/create-checkout-session",
headers={"x-api-key": API_KEY},
json={'price': 'price_1TsnweK6xZmbDQn2mpmU6Hkm', 'success_url': 'https://example.com/billing/success', 'cancel_url': 'https://example.com/billing/cancel'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/create-checkout-session", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"price": "price_1TsnweK6xZmbDQn2mpmU6Hkm", "success_url": "https://example.com/billing/success", "cancel_url": "https://example.com/billing/cancel"})
});
const data = await resp.json();{
"id": "cs_live_a1B2c3D4e5F6g7H8i9J0kLmNoPqRsTuVwXyZ0a1b2c3d4e5f6g7h8"
}Open the Stripe customer portal to manage billing: cancel at period end, update the card, or view invoices. Returns 404 with code NO_BILLING_ACCOUNT if the account has never checked out.
curl https://api.bottlebluellc.com/create-portal-session \n -X POST \n -H "x-api-key: $BBL_API_KEY" \n -H "Content-Type: application/json" \n -d '{"return_url": "https://app.bottlebluellc.com/subscription"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/create-portal-session",
headers={"x-api-key": API_KEY},
json={'return_url': 'https://app.bottlebluellc.com/subscription'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/create-portal-session", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"return_url": "https://app.bottlebluellc.com/subscription"})
});
const data = await resp.json();{
"url": "https://billing.stripe.com/p/session/live_a1B2c3D4e5F6g7H8"
}List available subscription plans and pricing.
curl https://api.bottlebluellc.com/get-subscription-plans \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/get-subscription-plans",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/get-subscription-plans", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();[
{
"name": "Free",
"displayOrder": 1,
"priceMonthly": 0,
"priceAnnual": 0,
"requestsPerMonth": 3000,
"rateLimitPerMin": 10,
"extras": "All APIs, core data",
"support": "Community support"
},
{
"name": "Hobby",
"displayOrder": 2,
"priceMonthly": 5,
"priceAnnual": 50,
"requestsPerMonth": 50000,
"rateLimitPerMin": 60,
"stripePriceIdMonthly": "price_1TsnwdK6xZmbDQn2utYAYr9n",
"stripePriceIdAnnual": "price_1TsnwdK6xZmbDQn2oAGoAHaD",
"extras": "All APIs, full data, commercial use",
"support": "Email support"
},
{
"name": "Pro",
"displayOrder": 3,
"badge": "Most Popular",
"priceMonthly": 25,
"priceAnnual": 250,
"requestsPerMonth": 300000,
"rateLimitPerMin": 300,
"stripePriceIdMonthly": "price_1TsnweK6xZmbDQn2mpmU6Hkm",
"stripePriceIdAnnual": "price_1TsnweK6xZmbDQn2gqVwfGhI",
"extras": "Premium fields, bulk endpoints, commercial use",
"support": "Priority support"
},
{
"name": "Enterprise",
"displayOrder": 4,
"contact": true,
"contactEmail": "bottlebluellc@gmail.com",
"priceAnchorMonthly": 299,
"extras": "Custom volume, custom rate limits, SLA",
"support": "Dedicated support"
}
]Get the caller's current subscription.
curl https://api.bottlebluellc.com/get-subscription-status \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/get-subscription-status",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/get-subscription-status", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"email": "you@example.com",
"auto_renew": true,
"renew_date": "2026-08-13",
"levels": [
"Pro",
"Free"
]
}Return API usage statistics for the account.
curl https://api.bottlebluellc.com/get-usage-stats \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api_key": "KEY"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/get-usage-stats",
headers={"x-api-key": API_KEY},
json={'api_key': 'KEY'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/get-usage-stats", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"api_key": "KEY"})
});
const data = await resp.json();{
"usage_stats": {
"total_requests": 42,
"average_latency_ms": 87.5,
"max_latency_ms": 412,
"min_latency_ms": 23,
"most_used_endpoints": [
[
"/market",
18
],
[
"/weather",
12
],
[
"/math",
6
]
],
"status_code_distribution": {
"200": 39,
"403": 2,
"500": 1
},
"hourly_distribution": {
"9": 10,
"13": 20,
"17": 12
},
"hourly_distribution_percent": {
"9": 23.80952380952381,
"13": 47.61904761904762,
"17": 28.571428571428573
},
"first_usage": "2026-06-09T08:12:45.123456",
"last_usage": "2026-07-08T13:58:02.654321",
"max_requests_per_day": 100,
"quota_usage_percent": 12.0,
"ip_address_stats": {
"unique_ip_count": 3,
"most_used_ips": [
[
"203.0.113.7",
30
],
[
"198.51.100.22",
10
],
[
"192.0.2.5",
2
]
]
},
"requests_today": 12
}
}Stripe webhook receiver (verified by Stripe signature).
curl https://api.bottlebluellc.com/stripe/webhook \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Stripe-Signature: t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd" \
-H "Content-Type: application/json" \
-d '{"id": "evt_1RiXaBK6xZmbDQn2mC4pQ7Ls", "type": "customer.subscription.updated", "data": {"object": {"customer_email": "you@example.com", "status": "active", "current_period_end": 1788705000}}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/stripe/webhook",
headers={"x-api-key": API_KEY, "Stripe-Signature": "t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},
json={'id': 'evt_1RiXaBK6xZmbDQn2mC4pQ7Ls', 'type': 'customer.subscription.updated', 'data': {'object': {'customer_email': 'you@example.com', 'status': 'active', 'current_period_end': 1788705000}}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/stripe/webhook", {
method: "POST",
headers: { "x-api-key": API_KEY, "Stripe-Signature": "t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd", "Content-Type": "application/json" },
body: JSON.stringify({"id": "evt_1RiXaBK6xZmbDQn2mC4pQ7Ls", "type": "customer.subscription.updated", "data": {"object": {"customer_email": "you@example.com", "status": "active", "current_period_end": 1788705000}}})
});
const data = await resp.json();Subscription status updatedSubscription lifecycle webhook receiver.
curl https://api.bottlebluellc.com/subscription-webhook \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Stripe-Signature: t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd" \
-H "Content-Type: application/json" \
-d '{"id": "evt_1RiXaBK6xZmbDQn2mC4pQ7Ls", "type": "customer.subscription.updated", "data": {"object": {"customer_email": "you@example.com", "status": "active", "current_period_end": 1788705000}}}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/subscription-webhook",
headers={"x-api-key": API_KEY, "Stripe-Signature": "t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"},
json={'id': 'evt_1RiXaBK6xZmbDQn2mC4pQ7Ls', 'type': 'customer.subscription.updated', 'data': {'object': {'customer_email': 'you@example.com', 'status': 'active', 'current_period_end': 1788705000}}},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/subscription-webhook", {
method: "POST",
headers: { "x-api-key": API_KEY, "Stripe-Signature": "t=1751980800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd", "Content-Type": "application/json" },
body: JSON.stringify({"id": "evt_1RiXaBK6xZmbDQn2mC4pQ7Ls", "type": "customer.subscription.updated", "data": {"object": {"customer_email": "you@example.com", "status": "active", "current_period_end": 1788705000}}})
});
const data = await resp.json();Subscription status updatedMarket data
Base URL https://api.bottlebluellc.com
Discovery index: every market and government dataset this API serves.
curl https://api.bottlebluellc.com/market \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/market", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"market_categories": {
"crypto": { "snapshot": "/market/crypto", "history": "/market/crypto/{ticker}" },
"stocks": { "snapshot": "/market/stocks", "history": "/market/stocks/{ticker}" },
"etfs": { "snapshot": "/market/etfs", "history": "/market/etfs/{ticker}" },
"rates": { "snapshot": "/market/rates", "history": "/market/rates/{series}" },
"futures": { "snapshot": "/market/futures", "history": "/market/futures/{symbol}" }
},
"government_datasets": {
"congress": { "snapshot": "/government/congress" },
"sec": { "snapshot": "/government/sec" }
},
"feeds": {
"news": { "endpoint": "/market/news" },
"poetry": { "endpoint": "/poetry" }
}
}Stored market headlines.
| Parameter | In | Description |
|---|---|---|
source | query | optional Filter by source. |
limit | query | optional Max items (default 25). |
curl https://api.bottlebluellc.com/market/news \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/news",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/market/news", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"sources": [
"MarketWatch",
"Yahoo Finance",
"Google News",
"Google News Tech"
],
"count": 2,
"items": [
{
"source": "MarketWatch",
"timestamp": "2026-07-08T12:31:04.118392",
"headline": "Stocks edge higher as June CPI cools more than expected",
"link": "https://www.marketwatch.com/story/stocks-edge-higher-as-june-cpi-cools-more-than-expected-2026-07-08"
},
{
"source": "Yahoo Finance",
"timestamp": "2026-07-08T11:58:47.902165",
"headline": "Fed officials signal patience on rate cuts ahead of FOMC minutes",
"link": "https://finance.yahoo.com/news/fed-officials-signal-patience-rate-cuts-115847123.html"
}
]
}Daily model-generated SPY predictions.
curl https://api.bottlebluellc.com/market/predictions \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/predictions",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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."
},
{
"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."
}
]
}Reddit trending-ticker scans.
curl https://api.bottlebluellc.com/market/trending \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/trending",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/market/trending", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"sources": [
"Reddit",
"Yahoo Finance"
],
"count": 2,
"items": [
{
"source": "Reddit",
"timestamp": "2026-07-08T10:30:04.512874",
"trending": "NVDA, TSLA, PLTR, AMD, SMCI, HOOD, GME, SOFI"
},
{
"source": "Reddit",
"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 includes a weekend-total row; 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/market/crypto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/crypto",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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": "yfinance"
},
{
"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": "yfinance"
}
]
}Historical time series for one instrument or series.
| 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/market/crypto/BTC-USD \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/crypto/BTC-USD",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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": "yfinance"
},
{
"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": "yfinance"
}
]
}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/market/earnings?days=7" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/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/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,
"source": "Nasdaq"
}
]
}Per-instrument statistics scored against 5 years of history.
Every instrument in the store is scored against its own 5-year history. Narrow with a source category and entity: /market/stats/{category} (a market slug, e.g. bonds) and /market/stats/{category}/{entity} (e.g. /market/stats/bonds/^TNX). 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 |
|---|---|---|
category | path | optional A market slug (bonds, commodities, crypto, ...). Omit for every instrument. |
entity | path | optional 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/market/stats/bonds/^TNX" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/stats/bonds/^TNX",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/market/stats/bonds/^TNX", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"scope": "bonds",
"entity": "^TNX",
"date": "2026-07-12",
"item": {
"date": "2026-07-12",
"label": "CBOE 10-Year Treasury Note Yield",
"entity": "^TNX",
"source_category": "BOND_YIELD",
"latest": 4.21,
"latest_date": "2026-07-11",
"observations": 1258,
"mean": 3.87,
"percentile": 71.5,
"zscore": 0.63,
"realized_vol_30d": 0.18,
"change_12m_pct": -4.2,
"high_52w": 4.79,
"low_52w": 3.62,
"lookback_years": 5
}
}Government data
Base URL https://api.bottlebluellc.com
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. |
curl https://api.bottlebluellc.com/government/congress \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/government/congress",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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
}Utilities
Base URL https://api.bottlebluellc.com
Practical calculator: geometry, finance, statistics, unit-aware percentages.
curl https://api.bottlebluellc.com/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/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/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"
}
}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/ntp \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/ntp",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/rng \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/rng",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/poetry \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/poetry",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/seconds/midnight?timezone=UTC \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/seconds/midnight?timezone=UTC",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/seconds/season \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/seconds/season",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/seconds/sunset \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/seconds/sunset",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/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/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/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
List the caller's notes.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/notes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/notes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/notes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/notes",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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 (also served on https://api.aquabotany.com)
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. * 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/links?from=bb:event:default:xyz789" \
-H "x-api-key: $BBL_API_KEY"
# What is this task part of?
curl "https://api.bottlebluellc.com/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/links?from=bb:event:default:xyz789&type=has_task" \
-H "x-api-key: $BBL_API_KEY"
curl "https://api.bottlebluellc.com/links?platform=*" \
-H "x-api-key: $BBL_API_KEY"import requests
BB = "https://api.bottlebluellc.com/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/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/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/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/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/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/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/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. |
curl "https://api.bottlebluellc.com/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/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/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
Named lists (to-do, shopping, etc.).
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/list?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/list",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/list",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/list",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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
Read a queue's contents, or purge it.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/queue/deploys",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/queue/deploys",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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
Read, update, or delete a user's streak.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/streaks/u123",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/streaks/u123/history",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug (streaks do not support "*"):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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
Submit a score to a leaderboard. On your own platform you may submit your own score; a Bottle Blue product-platform board is admin-managed and requires the x-admin-key header.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are admin managed and also require the x-admin-key header
curl https://api.bottlebluellc.com/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/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/leaderboard/score",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are admin managed and also require the x-admin-key header
json={'score': 4200, 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are admin managed and also require the x-admin-key header
body: JSON.stringify({"score": 4200, "platform": "my_garden"})
});{
"message": "Score submitted",
"user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
"score": 4200
}Top scores on a leaderboard.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/leaderboard/top",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are admin managed and also require the x-admin-key header
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
# product-platform boards are admin managed and also require the x-admin-key header
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
// product-platform boards are admin managed and also require the x-admin-key header
const scoped = await fetch("https://api.bottlebluellc.com/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
Read or delete a live scoreboard for a match.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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
List a user's assets.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/assets?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.bottlebluellc.com/assets",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.bottlebluellc.com/assets",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform) or a Bottle Blue product slug:
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/assets?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"assets": [
{
"entityType": "asset",
"type": "structure",
"assetType": "structure",
"name": "Garden shed",
"createdAt": "2026-07-08T09:30:12Z",
"metadata": {
"color": "green"
},
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07"
}
]
}Create an asset.
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/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/assets",
headers={"x-api-key": API_KEY},
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"entityType": "asset",
"type": "structure",
"assetType": "structure",
"name": "Garden shed",
"createdAt": "2026-07-08T09:30:12Z",
"metadata": {
"color": "green"
},
"assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07"
}Merge fields into an asset's metadata.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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"}}'
# Scoped to one platform
# platform can also be a Bottle Blue product slug ("*" is not allowed on writes):
# "bb_aquabotany" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl https://api.bottlebluellc.com/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/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07",
headers={"x-api-key": API_KEY},
json={'metadata': {'notes': 'Repainted this spring'}},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.patch(
"https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"metadata": {"notes": "Repainted this spring"}})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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"
}Delete an asset.
| Parameter | In | Description |
|---|---|---|
id | path | required Asset id. |
# Default namespace (platform omitted)
curl https://api.bottlebluellc.com/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
curl "https://api.bottlebluellc.com/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/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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "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/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" AquaBotany, "bb_pocketquant" Pocket Quant, "bb_bottleblue" Bottle Blue App, "bb_apparatus" Apparatus Ready, "bb_trackless" Trackless
const scoped = await fetch("https://api.bottlebluellc.com/assets/0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});{
"message": "Asset deleted"
}Calculators
Base URL https://api.bottlebluellc.com
Deterministic, pure-compute finance, fitness, and real-estate calculators - no upstream, always available. All inputs are query-string parameters.
Discovery: catalog of every finance / fitness / real-estate calculator + its parameters.
curl "https://api.bottlebluellc.com/calculate" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/calculate",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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/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/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/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",
"result": {
"monthly_payment": 1896.2,
"number_of_payments": 360,
"total_paid": 682633.47,
"total_interest": 382633.47
}
}Fitness calculators (pure-compute): bmi, bmr, tdee, body-fat, macros, one-rep-max, calories-burned, ideal-weight.
curl "https://api.bottlebluellc.com/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/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/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",
"result": {
"bmr_kcal_per_day": 1780,
"tdee_kcal_per_day": 2759,
"activity_multiplier": 1.55
}
}Real-estate calculators (pure-compute): cap-rate, cash-on-cash, noi, gross-rent-multiplier, dscr, affordability, price-per-sqft, rent-vs-buy.
curl "https://api.bottlebluellc.com/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/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/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",
"result": { "cap_rate_pct": 6.0, "noi": 24000.0 }
}Market analysis
Base URL https://api.bottlebluellc.com
One-call macro dashboard: latest interest rates, economic indicators, national debt, and market sentiment.
curl "https://api.bottlebluellc.com/market/econ-summary" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/econ-summary",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/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 (indices, sector ETFs, tracked crypto) use our own history; other symbols resolve through free upstreams.
curl "https://api.bottlebluellc.com/market/technicals/^GSPC" \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/market/technicals/^GSPC",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/market/technicals/^GSPC", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"ticker": "^GSPC",
"source": "scrape",
"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" }
}Admin
Base URL https://api.bottlebluellc.com
Roles and permissions (RBAC)
Bottle Blue product platforms can define named roles and assign them to users by email, so an app can offer access tiers such as admin, editor, and viewer over its own pages and resources. This is separate from billing tiers (Free / Hobby / Pro / Enterprise) and from data segmentation (the platform field): it answers whether a given user is allowed to perform a given action in your app.
How it works
- A role is a set of permission strings shaped
resource:action, for examplenotes:read,notes:write, orpages/dashboard:read. - Wildcards are supported:
*or*:*grant everything,resource:*grants every action on a resource, and*:actiongrants an action across resources. - A member is assigned one or more roles; their effective permissions are the union of those roles.
- Your app enforces a permission with
GET /access/check, or server-side in your own backend.
These endpoints are operator-only: they are gated by the static x-admin-key header (not a normal API key) and are available only for registered Bottle Blue product platforms.
Roles & permissions
Base URL https://api.bottlebluellc.com
List the roles defined for a product platform. Operator-only (x-admin-key).
| Parameter | In | Description |
|---|---|---|
platform | query | Required. A registered Bottle Blue product platform id. |
curl "https://api.bottlebluellc.com/access/roles?platform=bb_aquabotany" \
-H "x-admin-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/access/roles",
headers={"x-admin-key": ADMIN_KEY},
params={'platform': 'bb_aquabotany'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/roles?platform=bb_aquabotany", {
method: "GET",
headers: { "x-admin-key": ADMIN_KEY }
});
const data = await resp.json();{
"platform": "100000000000001",
"roles": [
{ "role": "editor", "permissions": ["notes:*", "pages/dashboard:read"], "updatedAt": "2026-07-17T14:00:00+00:00" }
]
}Create or replace a role and its permissions. Operator-only (x-admin-key).
curl -X POST https://api.bottlebluellc.com/access/roles \
-H "x-admin-key: $BBL_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "bb_aquabotany", "role": "editor", "permissions": ["notes:*", "pages/dashboard:read"]}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/access/roles",
headers={"x-admin-key": ADMIN_KEY},
json={'platform': 'bb_aquabotany', 'role': 'editor', 'permissions': ['notes:*', 'pages/dashboard:read']},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/roles", {
method: "POST",
headers: { "x-admin-key": ADMIN_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ platform: "bb_aquabotany", role: "editor", permissions: ["notes:*", "pages/dashboard:read"] })
});
const data = await resp.json();{
"platform": "100000000000001",
"role": "editor",
"permissions": ["notes:*", "pages/dashboard:read"],
"updatedAt": "2026-07-17T14:00:00+00:00"
}Delete a role. Operator-only (x-admin-key).
curl -X DELETE https://api.bottlebluellc.com/access/roles \
-H "x-admin-key: $BBL_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "bb_aquabotany", "role": "editor"}'import requests
resp = requests.delete(
"https://api.bottlebluellc.com/access/roles",
headers={"x-admin-key": ADMIN_KEY},
json={'platform': 'bb_aquabotany', 'role': 'editor'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/roles", {
method: "DELETE",
headers: { "x-admin-key": ADMIN_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ platform: "bb_aquabotany", role: "editor" })
});
const data = await resp.json();{
"platform": "100000000000001",
"role": "editor",
"deleted": true
}List members of a product platform and the roles assigned to each. Operator-only (x-admin-key).
| Parameter | In | Description |
|---|---|---|
platform | query | Required. A registered Bottle Blue product platform id. |
curl "https://api.bottlebluellc.com/access/members?platform=bb_aquabotany" \
-H "x-admin-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/access/members",
headers={"x-admin-key": ADMIN_KEY},
params={'platform': 'bb_aquabotany'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/members?platform=bb_aquabotany", {
method: "GET",
headers: { "x-admin-key": ADMIN_KEY }
});
const data = await resp.json();{
"platform": "100000000000001",
"members": [
{ "email": "user@example.com", "roles": ["editor"], "updatedAt": "2026-07-17T14:00:00+00:00" }
]
}Assign one or more roles to a member (by email). Operator-only (x-admin-key).
curl -X POST https://api.bottlebluellc.com/access/members \
-H "x-admin-key: $BBL_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "bb_aquabotany", "email": "user@example.com", "roles": ["editor"]}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/access/members",
headers={"x-admin-key": ADMIN_KEY},
json={'platform': 'bb_aquabotany', 'email': 'user@example.com', 'roles': ['editor']},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/members", {
method: "POST",
headers: { "x-admin-key": ADMIN_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ platform: "bb_aquabotany", email: "user@example.com", roles: ["editor"] })
});
const data = await resp.json();{
"platform": "100000000000001",
"email": "user@example.com",
"roles": ["editor"],
"updatedAt": "2026-07-17T14:00:00+00:00"
}Remove a member. Operator-only (x-admin-key).
curl -X DELETE https://api.bottlebluellc.com/access/members \
-H "x-admin-key: $BBL_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"platform": "bb_aquabotany", "email": "user@example.com"}'import requests
resp = requests.delete(
"https://api.bottlebluellc.com/access/members",
headers={"x-admin-key": ADMIN_KEY},
json={'platform': 'bb_aquabotany', 'email': 'user@example.com'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/members", {
method: "DELETE",
headers: { "x-admin-key": ADMIN_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ platform: "bb_aquabotany", email: "user@example.com" })
});
const data = await resp.json();{
"platform": "100000000000001",
"email": "user@example.com",
"deleted": true
}Test whether a member holds a permission (wildcard-aware). Operator-only (x-admin-key).
| Parameter | In | Description |
|---|---|---|
platform | query | Required. A registered Bottle Blue product platform id. |
email | query | Required. The member to test. |
permission | query | Required. A resource:action permission string. |
curl "https://api.bottlebluellc.com/access/check?platform=bb_aquabotany&email=user@example.com&permission=pages/dashboard:read" \
-H "x-admin-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/access/check",
headers={"x-admin-key": ADMIN_KEY},
params={'platform': 'bb_aquabotany', 'email': 'user@example.com', 'permission': 'pages/dashboard:read'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/access/check?platform=bb_aquabotany&email=user@example.com&permission=pages/dashboard:read", {
method: "GET",
headers: { "x-admin-key": ADMIN_KEY }
});
const data = await resp.json();{
"platform": "100000000000001",
"email": "user@example.com",
"permission": "pages/dashboard:read",
"allow": true
}First-party app authentication (user password → Bearer token)
x-api-key header is the credential sold to end-users through the subscription flow. When you build a first-party app (AquaBotany web, Pocket Quant web, Bottle Blue App, etc.) you should authenticate as the logged-in Cognito user instead - never ship an API key inside a client app you own.How it works
The auth gateway accepts two credential sources on every gated endpoint:
- x-api-key header - looks up the key, resolves the owner email, checks status/expiry.
- Authorization: Bearer <access_token> - calls Cognito
GetUser(AccessToken=…)server-side to verify the token and resolve the email. No signature key needed client-side; Cognito validates it.
Cognito is preferred by default. Either path resolves to an email, which is then used for subscription and rate-limit checks identically.
Sign-in flow (POST /auth/signin)
The sign-in endpoint calls Cognito USER_PASSWORD_AUTH and returns three tokens plus a server-side session id:
curl https://api.bottlebluellc.com/auth/signin \
-X POST \
-H "Content-Type: application/json" \
-d '{
"username": "you@example.com",
"password": "YourPassword123!",
"platform": "bb_aquabotany", "client": "web"
}'
# Response:
# {
# "session_id": "<server session uuid>",
# "id_token": "<Cognito JWT - identity claims>",
# "access_token": "<Cognito JWT - USE THIS for Bearer auth>",
# "refresh_token": "<long-lived token to get new access tokens>"
# }import requests
# Step 1 - sign in
signin = requests.post(
"https://api.bottlebluellc.com/auth/signin",
json={
"username": "you@example.com",
"password": "YourPassword123!",
"platform": "bb_aquabotany", "client": "web", # platform = product, client = web or mobile
},
)
tokens = signin.json()
access_token = tokens["access_token"]
# Step 2 - call any gated endpoint as yourself
resp = requests.get(
"https://api.aquabotany.com/weather",
headers={"Authorization": f"Bearer {access_token}"},
params={"lat": 44.98, "lon": -93.26},
)
print(resp.json())// Step 1 - sign in
const signin = await fetch("https://api.bottlebluellc.com/auth/signin", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: "you@example.com",
password: "YourPassword123!",
platform: "bb_aquabotany", client: "web",
}),
});
const { access_token, refresh_token } = await signin.json();
// Step 2 - call any gated endpoint as yourself
const resp = await fetch("https://api.aquabotany.com/weather?lat=44.98&lon=-93.26", {
headers: { Authorization: `Bearer ${access_token}` },
});
console.log(await resp.json());Valid platform values
Sign-in takes two separate fields. platform is the Bottle Blue product namespace the login belongs to, and it is the same value you send as platform on building-block calls. It is required and must be one of:
| Value | Product |
|---|---|
bb_aquabotany | AquaBotany |
bb_bottleblue | Bottle Blue App |
bb_pocketquant | Pocket Quant |
bb_apparatus | Apparatus Ready |
bb_trackless | Trackless |
client says which kind of app the login came from: web or mobile. It never affects data. Sessions are scoped to the pair, so the same account signed in on web and on mobile holds two independent sessions and signing out of one leaves the other alone. It is required on POST /auth/signin and POST /auth/signout, which create and delete a session, and optional on the read-only POST /auth/status and POST /auth/refresh.
The sign-in response echoes the product namespace back as data_platform, which is the value to send as platform on subsequent building-block calls so the app's data lands in its own namespace. It is the same slug you signed in with, and it is also returned by POST /auth/refresh and POST /auth/status. Every product also has an internal numeric id; the two name the same namespace and the slug is the public way to address it.
Token lifecycle
Cognito access tokens expire (typically 1 hour). Use the refresh token with POST /auth/refresh to get a new access token without re-entering the password. The server-side session_id returned at sign-in is stored server-side (7-day TTL) and is used by webapp session-cookie flows; you do not need it if you are managing Cognito tokens directly.
When to use which credential
| Scenario | Use | Why |
|---|---|---|
| End-user product (customer of the platform) | x-api-key | Purchased through subscription; rate-limited per key |
| First-party app you own (AquaBotany, Pocket Quant, BBL App) | Authorization: Bearer <access_token> | Authenticates as the actual logged-in user; no key to manage or expose |
| Server-side scripts / AI agents acting as yourself | Bearer token from a fresh sign-in | Full access, tied to your Cognito account, no subscription billing |
refresh_token itself should be stored securely (env var / secrets manager) and never logged.(Admin) List every API key in the system.
curl https://api.bottlebluellc.com/admin/get-all-keys \
-H "x-api-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/admin/get-all-keys",
headers={"x-api-key": ADMIN_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/admin/get-all-keys", {
method: "GET",
headers: { "x-api-key": ADMIN_KEY }
});
const data = await resp.json();{
"keys": [
{
"PK": "apikey#3f9c2a7e-8d41-4b6a-9c53-2e7f0d1a5b88",
"api_key": "3f9c2a7e-8d41-4b6a-9c53-2e7f0d1a5b88",
"email": "you@example.com",
"api_key_name": "default",
"key_status": "active",
"role": "admin",
"multi_key": false,
"auto_renew": false,
"created_at": "2026-06-15T09:24:11.532918",
"expires_at": 1784107451,
"group_id": "1"
},
{
"PK": "apikey#9b2d6f04-1c7a-4e39-8a52-d0e4b6c81f27",
"api_key": "9b2d6f04-1c7a-4e39-8a52-d0e4b6c81f27",
"email": "you@example.com",
"api_key_name": "ci-runner",
"key_status": "inactive",
"role": "admin",
"multi_key": false,
"auto_renew": false,
"created_at": "2026-05-02T18:07:44.120553",
"expires_at": 1780337264,
"group_id": "1"
}
]
}(Admin) Read system access logs.
curl https://api.bottlebluellc.com/admin/get-logs \
-H "x-api-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/admin/get-logs",
headers={"x-api-key": ADMIN_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/admin/get-logs", {
method: "GET",
headers: { "x-api-key": ADMIN_KEY }
});
const data = await resp.json();{
"abuse_logs": [
{
"PK": "abuse#7c1e5b0a-3d2f-4e8b-a6c9-91f4d2e7b350",
"SK": "2026-07-06T21:14:53.208441",
"ip_address": "203.0.113.42",
"reason": "Unauthorized: Invalid or inactive API key",
"api_key": "not-a-real-key",
"additional_info": null
},
{
"PK": "abuse#f4a09d6e-52b8-47c3-b1e7-6d3a80c92514",
"SK": "2026-07-07T04:41:09.887210",
"ip_address": "198.51.100.17",
"reason": "Unauthorized: Forbidden: API key has been revoked",
"api_key": "9b2d6f04-1c7a-4e39-8a52-d0e4b6c81f27",
"additional_info": null
}
]
}(Admin) Send an email.
curl https://api.bottlebluellc.com/admin/message/email \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sender": "support@aquabotany.com", "recipient": "user@example.com", "subject": "Hello", "body_text": "Hi there"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/admin/message/email",
headers={"x-api-key": API_KEY},
json={'sender': 'support@aquabotany.com', 'recipient': 'user@example.com', 'subject': 'Hello', 'body_text': 'Hi there'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/admin/message/email", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"sender": "support@aquabotany.com", "recipient": "user@example.com", "subject": "Hello", "body_text": "Hi there"})
});
const data = await resp.json();{
"message": "Email sent successfully",
"messageId": "0100019789a4b2c3-7f3e5d1a-42b6-4c8f-9a0d-6e2b8c4f1d35-000000"
}(Admin) Send an SMS. Not yet implemented (returns 501).
curl https://api.bottlebluellc.com/admin/message/sms \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recipient": "+15551234567", "message": "Hi"}'import requests
resp = requests.post(
"https://api.bottlebluellc.com/admin/message/sms",
headers={"x-api-key": API_KEY},
json={'recipient': '+15551234567', 'message': 'Hi'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/admin/message/sms", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"recipient": "+15551234567", "message": "Hi"})
});
const data = await resp.json();{
"error": "SMS sending is not implemented yet",
"code": "NOT_IMPLEMENTED",
"detail": "The /admin/message/sms endpoint is a stub. Use /admin/message/email for email until SMS is wired up."
}(Admin) Aggregated system usage metrics.
curl https://api.bottlebluellc.com/admin/metrics \
-H "x-api-key: $BBL_ADMIN_KEY"import requests
resp = requests.get(
"https://api.bottlebluellc.com/admin/metrics",
headers={"x-api-key": ADMIN_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.bottlebluellc.com/admin/metrics", {
method: "GET",
headers: { "x-api-key": ADMIN_KEY }
});
const data = await resp.json();{
"metrics": {
"total_requests": 18432,
"unique_ips": 76,
"endpoints": [
"/market",
"/weather",
"/ntp",
"/notes",
"/validate-key"
]
}
}Weather
Base URL https://api.aquabotany.com
Current conditions and forecast for a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/weather?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/weather?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",
"timezone": "America/Chicago"
},
"current": {
"temp_f": 72.4,
"temp_c": 22.4,
"humidity_pct": 58,
"wind_mph": 9.2,
"wind_dir": "SSW",
"condition": "Partly Cloudy",
"uv_index": 5
},
"updated": "2026-07-07T14:30:00Z",
"requestId": "req_9c2f7b1d4a8e"
}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/weather/alerts?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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."
}
],
"source": "nws"
}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/weather/forecast?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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
}
],
"source": "nws"
}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/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/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/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
}
],
"source": "open-meteo (ERA5)"
}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/weather/product \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/weather/product",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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",
"source": "nws"
}Seasonal gardening advice for the location.
curl https://api.aquabotany.com/weather/seasonal-advice?lat=44.98 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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/weather/zone?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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/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/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/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
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/environment/air?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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/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/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/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
}
],
"source": "open-meteo",
"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/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/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/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
}
],
"source": "open-meteo",
"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/environment/earthquakes \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/environment/earthquakes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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
}
],
"source": "usgs"
}Terrain elevation at a coordinate.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/environment/elevation?lat=44.98&lon=-93.26 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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,
"source": "open-meteo"
}Terrain elevation for a named place.
| Parameter | In | Description |
|---|---|---|
name | query | required Place name. |
curl https://api.aquabotany.com/environment/elevation-by-place?name=Kyoto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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,
"source": "open-meteo"
}Active natural-hazard events across all categories (storms, floods, volcanoes, ...).
| Parameter | In | Description |
|---|---|---|
category | query | optional EONET category id (e.g. severeStorms, volcanoes, floods); 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/environment/events \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/environment/events",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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"
]
}
],
"source": "nasa-eonet",
"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/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/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/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/environment/moon \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/environment/moon",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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/environment/pollen?lat=52.52&lon=13.41 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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",
"source": "open-meteo"
}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/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/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/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"
}
}
}
],
"source": "open-meteo",
"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/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/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/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"
}
}
}
],
"source": "open-meteo",
"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/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/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/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)",
"source": "isric-soilgrids"
}Live soil temperature and moisture at several depths.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/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/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/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
}
},
"source": "open-meteo"
}Numeric soil properties (pH, organic carbon, ...).
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/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/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/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/environment/wildfire \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/environment/wildfire",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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"
}
],
"source": "nasa-eonet"
}Nearby recycling drop-off locations.
curl https://api.aquabotany.com/sustainability/recycling-locations \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/sustainability/recycling-locations",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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/sustainability/tips?material=plastic \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/sustainability/tips?material=plastic",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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
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/location/geocode?name=Kyoto \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/location/geocode?name=Kyoto",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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
}
],
"source": "open-meteo"
}Resolve a coordinate to the nearest address.
| Parameter | In | Description |
|---|---|---|
lat | query | required Latitude. |
lon | query | required Longitude. |
curl https://api.aquabotany.com/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/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/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"
},
"source": "openstreetmap"
}Nearest Kwik Trip / Kwik Star stores to a coordinate.
| 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/places/kwik-trip?lat=44.98&lon=-93.26&limit=2 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/places/kwik-trip?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/places/kwik-trip?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"
}
],
"source": "kwiktrip"
}Plants
Base URL https://api.aquabotany.com
List the caller's plants.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/my-plants \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/my-plants",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/my-plants",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/my-plants", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/my-plants/3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/my-plants/9d4b8e02-6f1a-47c5-b3e8-51a0c2d97f46",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/plants/plantfinder?query_type=zone&filter_value=4 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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/plants/tips?plant=monstera \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/plants/tips?plant=monstera",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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
Registered garden automation devices.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/automation-devices \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/automation-devices",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/automation-devices",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/automation-devices", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/automation-devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/devices/b7e9d2c4-5a1f-4e8b-9c3d-2f6a8e1b4d70/commands/7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/garden-environment \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/garden-environment",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/garden-environment",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/garden-environment", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/garden-environment/c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/sensor-data?deviceId=SN-00142 \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/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/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/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/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/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
Reminders (legacy collection).
# Default namespace (platform omitted)
curl https://api.aquabotany.com/reminders \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/reminders?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/reminders",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/reminders",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/reminders/9f2c4b7d8a1e4f60b3c5d7e9a2f14c86?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});List gardening tasks.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/tasks \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/tasks?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/tasks",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/tasks",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks/completed",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks/completed", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/tasks/reminders \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks/reminders", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks/reminders",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks/reminders", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
"https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
"https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/tasks/d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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
List community articles.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/articles \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/articles?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/articles",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/articles",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/articles?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
}Publish an article.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/articles", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/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 AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/comments/5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/likes",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/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 AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/comments \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/comments?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/comments",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/comments",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/feed \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/feed?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/feed",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/feed",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/likes \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/likes?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/likes",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/likes",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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
platform. Writing to a Bottle Blue product platform (the first-party apps' numeric platform ids) requires the admin key (x-admin-key); without it a create/update/delete returns 403. On your own project platform (or the default platform) you may create freely, but you can only edit, complete, or delete a row you created; acting on another user's row returns 403 NOT_OWNER. Reads are unaffected.Trackable eco/garden actions.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/actions \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/actions?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/actions",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/actions",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/actions?platform=my_garden", {
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",
"actionType": "watering",
"description": "Watered garden",
"metadata": {}
},
{
"userId": "coughlin058@gmail.com",
"actionId": "4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11",
"timestamp": "2026-07-07T18:02:11.905417",
"actionType": "composting",
"description": "Added kitchen scraps to compost bin",
"metadata": {}
}
],
"nextKey": null
}Trackable eco/garden actions.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/actions \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actionType": "watering", "description": "Watered garden", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/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/actions",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'actionType': 'watering', 'description': 'Watered garden', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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/actions", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"actionType": "watering", "description": "Watered garden", "platform": "my_garden"})
});{
"message": "Action logged",
"actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02"
}The caller's action progress.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/actions/user-progress \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/actions/user-progress",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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",
"actionType": "watering",
"description": "Watered garden",
"metadata": {}
}Read, update, or delete an action.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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",
"actionType": "watering",
"description": "Watered the herb garden beds",
"metadata": {}
}Read, update, or delete an action.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/actions/8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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",
"actionType": "pruning",
"description": "Watered garden",
"metadata": {}
}Read, update, or delete an action.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/actions/4c9b1e6d-2f8a-47c3-b0e5-6d7a2c4f8e11",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/challenges \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/challenges?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/challenges",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/challenges",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/challenges",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/challenges", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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"
}Currently-active challenges.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/challenges/active \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/challenges/active",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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"
}
]Past challenges.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/challenges/history \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/challenges/history",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/challenges/7b3f1d8e-9a2c-4c6b-8e0d-5f4a1c9b7e22?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});Mark a challenge complete.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/challenges/2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15/complete?platform=my_garden", {
method: "POST",
headers: { "x-api-key": API_KEY }
});{
"message": "Challenge marked as completed"
}Community events.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/events \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/events?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/events",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/events",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/events",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/events", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/events/upcoming \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/events/upcoming",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/events/a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/rewards \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/rewards?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/rewards",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/rewards",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/rewards",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/rewards", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/rewards/history \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/rewards/history",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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/rewards/points \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/rewards/points",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/rewards/points", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"points": 175
}Add points to the caller's balance.
curl https://api.aquabotany.com/rewards/points/add \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 50}'import requests
resp = requests.post(
"https://api.aquabotany.com/rewards/points/add",
headers={"x-api-key": API_KEY},
json={'amount': 50},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/rewards/points/add", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"amount": 50})
});
const data = await resp.json();{
"points": 225
}Redeem points for a reward.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/rewards/redeem",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/rewards/redeem", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/rewards/c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/rewards/e91f4b28-6a0d-47c3-b8e2-1c5d9a3f7b04?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});Notifications
Base URL https://api.aquabotany.com
Create a notification for the authenticated account.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/notifications",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/notifications", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/notifications/you@example.com",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/notifications/you@example.com",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/notifications/you@example.com", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/notifications/you@example.com/read-all \
-X POST \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/notifications/you@example.com/read-all",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8 \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/notifications/you@example.com/9f3c1a2b4d5e6f708192a3b4c5d6e7f8?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});Content & settings
Base URL https://api.aquabotany.com
Submit a prompt to the async AI assistant. Admin-only: requires the x-admin-key header, and is capped at 3 jobs per caller per day.
curl https://api.aquabotany.com/ai/requests \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "When should I plant garlic in zone 4b?"}'import requests
resp = requests.post(
"https://api.aquabotany.com/ai/requests",
headers={"x-api-key": API_KEY},
json={'prompt': 'When should I plant garlic in zone 4b?'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/ai/requests", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"prompt": "When should I plant garlic in zone 4b?"})
});
const data = await resp.json();{
"requestId": "b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e"
}Poll an AI request's result. Admin-only: requires the x-admin-key header.
curl https://api.aquabotany.com/ai/requests/b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/ai/requests/b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/ai/requests/b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"status": "complete",
"prompt": "When should I plant garlic in zone 4b?",
"result": "In zone 4b, plant garlic in the fall, typically late September through mid-October, about 2-3 weeks before the ground freezes. This gives cloves time to root before winter. Mulch with 4-6 inches of straw and harvest the following July when the lower leaves brown.",
"timestamp": "2026-07-08T14:13:02.918274",
"requestId": "b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e"
}Get a presigned upload target. Upload the file by POSTing multipart/form-data to uploadUrl with every uploadFields entry first and the file last; S3 enforces the maxSizeMB limit and rejects anything larger. Then register the image with POST /images (plantId is required only when type is plant).
curl https://api.aquabotany.com/images?action=upload-url&type=plant&contentType=image/jpeg \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/images?action=upload-url&type=plant&contentType=image/jpeg",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/images?action=upload-url&type=plant&contentType=image/jpeg", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"uploadUrl": "https://aquabotany-images.s3.ap-northeast-1.amazonaws.com/",
"uploadFields": {
"Content-Type": "image/jpeg",
"key": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f",
"AWSAccessKeyId": "AKIA6EXAMPLE",
"policy": "eyJleHBpcmF0aW9uIjogIjIwMjYtMDctMDhUMTQ6MTg6MzBaIiwgImNvbmRpdGlvbnMiOiBbeyJDb250ZW50LVR5cGUiOiAiaW1hZ2UvanBlZyJ9LCBbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwgMSwgNTI0Mjg4MF1dfQ==",
"signature": "8f14e45fceea167a5a36dedd4bea2543a1b2c3d4"
},
"uploadMethod": "POST",
"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f",
"imageKey": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f",
"type": "plant",
"maxSizeMB": 5
}Image catalog / uploads.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/images \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "imageKey": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "plantId": "plant-123", "type": "plant"}'
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/images \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "imageKey": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "plantId": "plant-123", "type": "plant", "platform": "my_garden"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/images",
headers={"x-api-key": API_KEY},
json={'imageId': '7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f', 'imageKey': 'images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f', 'plantId': 'plant-123', 'type': 'plant'},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.post(
"https://api.aquabotany.com/images",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
json={'imageId': '7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f', 'imageKey': 'images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f', 'plantId': 'plant-123', 'type': 'plant', 'platform': 'my_garden'},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/images", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "imageKey": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "plantId": "plant-123", "type": "plant"})
});
const data = await resp.json();
// Scoped to one platform
const scoped = await fetch("https://api.aquabotany.com/images", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
body: JSON.stringify({"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "imageKey": "images/you@example.com/plant/7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f", "plantId": "plant-123", "type": "plant", "platform": "my_garden"})
});{
"message": "Image metadata saved",
"imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f"
}Educational gardening resources.
curl https://api.aquabotany.com/learning-resources \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/learning-resources",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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": "AquaBotany 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/"
}
]AquaBotany marketplace listings.
curl https://api.aquabotany.com/marketplace \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/marketplace",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/marketplace", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"plants": [
{
"id": "pilea-peperomioides",
"name": "Pilea Peperomioides",
"nickname": "Chinese Money Plant, UFO Plant",
"price": 749.89,
"imageUrls": [
"https://yourcdn.com/images/pilea_1.jpg",
"https://yourcdn.com/images/pilea_2.jpg",
"https://yourcdn.com/images/pilea_3.jpg"
],
"vaseColors": [
"#D7CBB1",
"#777C6A",
"#383834"
],
"lightRequirements": {
"ideal": "Bright, indirect sunlight (near a north or east-facing window).",
"avoid": "Direct afternoon sun \u2014 it can scorch the leaves.",
"tip": "Rotate the plant weekly to keep its growth symmetrical, since it tends to lean toward the light."
},
"humidityRequirements": {
"ideal": "Average household humidity (40\u201360%).",
"tolerates": "Slightly lower humidity, but does better with occasional misting or being near other plants.",
"boost": "If your air is very dry, group plants together or use a pebble tray/humidifier nearby."
},
"description": "A unique and popular houseplant known for its round, coin-shaped leaves. Great for beginners and thrives in bright, indirect light.",
"category": "Indoor",
"quantityAvailable": 12
},
{
"id": "example-plant-2",
"name": "Ficus Elastica",
"nickname": "Rubber Plant",
"price": 750,
"imageUrls": [
"https://yourcdn.com/images/ficus.jpg"
],
"vaseColors": [
"#B6B6B6",
"#5F5F5F",
"#2E2E2E"
],
"category": "Indoor",
"description": "A hardy plant with shiny, dark green leaves. Tolerates a range of conditions and purifies the air.",
"quantityAvailable": 8
}
]
}Read all account settings.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/settings \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/settings?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/settings",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/settings",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/settings",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/settings", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/settings/units",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/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/settings/units \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/settings/units",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/settings/units", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();
// Scoped to one platform
// platform can also be the AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/settings/units?platform=my_garden", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});AquaBotany account
Base URL https://api.aquabotany.com
The caller's dashboard summary.
curl https://api.aquabotany.com/dashboard \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/dashboard",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/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
}
]
}
}
}
]Get the caller's subscription.
curl https://api.aquabotany.com/subscription \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/subscription",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/subscription", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"email": "you@example.com",
"plan_id": "plan_pro",
"start_date": "2026-07-08T14:16:44.108829",
"status": "active"
}Create the subscription record.
curl https://api.aquabotany.com/subscription \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"plan_id": "plan_pro"}'import requests
resp = requests.post(
"https://api.aquabotany.com/subscription",
headers={"x-api-key": API_KEY},
json={'plan_id': 'plan_pro'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/subscription", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"plan_id": "plan_pro"})
});
const data = await resp.json();{
"email": "you@example.com",
"plan_id": "plan_pro",
"start_date": "2026-07-08T14:16:44.108829",
"status": "active"
}Change the subscription's plan or status.
curl https://api.aquabotany.com/subscription \
-X PUT \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"plan_id": "plan_pro", "status": "active"}'import requests
resp = requests.put(
"https://api.aquabotany.com/subscription",
headers={"x-api-key": API_KEY},
json={'plan_id': 'plan_pro', 'status': 'active'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/subscription", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"plan_id": "plan_pro", "status": "active"})
});
const data = await resp.json();{
"message": "Subscription updated"
}Cancel (delete) the subscription record.
curl https://api.aquabotany.com/subscription \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.delete(
"https://api.aquabotany.com/subscription",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/subscription", {
method: "DELETE",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"message": "Subscription deleted"
}Available AquaBotany plans.
curl https://api.aquabotany.com/subscription-plans \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.aquabotany.com/subscription-plans",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.aquabotany.com/subscription-plans", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"plans": [
{
"id": "plan_3_months",
"duration_months": 3,
"price": 299,
"savings": "0%",
"label": null
},
{
"id": "plan_6_months",
"duration_months": 6,
"price": 549,
"savings": "25%",
"label": null
},
{
"id": "plan_12_months",
"duration_months": 12,
"price": 959,
"savings": "40%",
"label": "Most Popular"
}
]
}Work orders
Base URL https://api.aquabotany.com
List the caller's work orders. Returns an array of {id, title, department, status, priority, assigned_to, created_at, updated_at}. Scoped to the authenticated user.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/workorder \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/workorder?platform=my_garden" \
-H "x-api-key: $BBL_API_KEY"import requests
# Default namespace (platform omitted)
resp = requests.get(
"https://api.aquabotany.com/workorder",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())
# Scoped to one platform
resp = requests.get(
"https://api.aquabotany.com/workorder",
headers={"x-api-key": API_KEY},
# platform can also be "*" (read across every platform)
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/workorder?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});[
{
"id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
"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",
"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"
}
]Create a work order. Body: title (required), department (required), assigned_to, priority (default Normal), notes. New orders get status New. Returns {"status": "created", "workorder_id": "<uuid>"}. 400 if title or department is missing.
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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"}'import requests
# Default namespace (platform omitted)
resp = requests.post(
"https://api.aquabotany.com/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/workorder",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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/workorder", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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"})
});{
"status": "created",
"workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}Get a single work order. Returns capitalized fields: Title, Department, AssignedTo, Priority, Status, Notes, CreatedAt, UpdatedAt, WorkOrderId, CreatedBy. 404 if not found or owned by another user.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
curl "https://api.aquabotany.com/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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform must match where the item lives: a project name
# or the AquaBotany product slug "bb_aquabotany"
params={"platform": "my_garden"},
)// Default namespace (platform omitted)
const resp = await fetch("https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
const scoped = await fetch("https://api.aquabotany.com/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f?platform=my_garden", {
method: "GET",
headers: { "x-api-key": API_KEY }
});{
"Type": "WorkOrder",
"WorkOrderId": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
"Title": "Replace irrigation pump",
"Department": "Maintenance",
"AssignedTo": null,
"Priority": "High",
"Status": "New",
"Notes": "Pump failed on section 3.",
"CreatedAt": "2026-07-08T14:18:02.331674+00:00",
"UpdatedAt": "2026-07-08T14:18:02.331674+00:00",
"CreatedBy": "you@example.com"
}Update a work order. Body: any of title, department, assigned_to, priority, status, notes. 400 if no fields supplied. Returns {"status": "updated", ...}.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PUT",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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"
}Same as PUT. Body: any of title, department, assigned_to, priority, status, notes. 400 if no fields supplied. Returns {"status": "updated", ...}.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/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 AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl https://api.aquabotany.com/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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f", {
method: "PATCH",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
// platform can also be the AquaBotany 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"
}Delete a work order. Returns {"status": "deleted", ...}. Scoped to the authenticated user; another user's IDs return 404.
| Parameter | In | Description |
|---|---|---|
workorderId | path | required Work order UUID. |
# Default namespace (platform omitted)
curl https://api.aquabotany.com/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f \
-X DELETE \
-H "x-api-key: $BBL_API_KEY"
# Scoped to one platform
# platform can also be the AquaBotany product slug "bb_aquabotany"
# ("*" is not allowed on writes)
curl "https://api.aquabotany.com/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/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/workorder/5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f",
headers={"x-api-key": API_KEY},
# platform can also be the AquaBotany 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/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 AquaBotany product slug "bb_aquabotany"
// ("*" is not allowed on writes)
const scoped = await fetch("https://api.aquabotany.com/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"
}Pocket Quant
Base URL https://api.pocketquant.io
Currently unavailable: the api.pocketquant.io host does not resolve yet, so the endpoints below are not reachable at that URL. Contact support for the current invoke URL if you need early access.
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();{
"message": "Dashboard"
}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();{
"status": "ok",
"service": "Pocket Quant API",
"message": "API is healthy and responding.",
"timestamp": "2026-07-08T14:32:07.412896+00:00"
}Pocket Quant mobile dashboard payload.
curl https://api.pocketquant.io/mobile \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://api.pocketquant.io/mobile",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/mobile", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"bedtimeStory": {
"id": "story_001",
"title": "Some Title",
"pages": [
"Page one text...",
"Page two text...",
"..."
]
},
"utc_datetime": "2026-07-08T14:32:07.518204+00:00",
"temperaments": {
"cheerful": {
"daily_greeting": "Hey sunshine! Ready for a wonderful day?",
"options": [
{
"option": 1,
"name": "Sing a Song",
"text": "Spread joy through music."
},
{
"option": 2,
"name": "Dance Party",
"text": "Dance like nobody's watching."
},
{
"option": 3,
"name": "Kindness Quest",
"text": "Make someone's day brighter."
},
{
"option": 4,
"name": "Adventure Walk",
"text": "Explore the world with a smile."
}
],
"fun_fact": "Smiling can trick your brain into feeling happier."
},
"stoic": {
"daily_greeting": "Calm waters run deep.",
"options": [
{
"option": 1,
"name": "Silent Vigil",
"text": "Stand strong, observing the world."
},
{
"option": 2,
"name": "Meditation",
"text": "Find calm amidst chaos."
},
{
"option": 3,
"name": "Training Regimen",
"text": "Sharpen body and mind alike."
},
{
"option": 4,
"name": "Path of Honor",
"text": "Walk the road less traveled with dignity."
}
],
"fun_fact": "Marcus Aurelius was a Roman emperor and a Stoic philosopher."
},
"mysterious": {
"daily_greeting": "The unknown awaits you, curious soul.",
"options": [
{
"option": 1,
"name": "Whispered Alley",
"text": "Follow hidden paths."
},
{
"option": 2,
"name": "Ancient Doorway",
"text": "Unlock forgotten secrets."
},
{
"option": 3,
"name": "Twilight Watch",
"text": "Observe what others overlook."
},
{
"option": 4,
"name": "Secret Gathering",
"text": "Meet the unseen movers of fate."
}
],
"fun_fact": "Crows can recognize human faces and hold grudges."
},
"grumpy": {
"daily_greeting": "Another day? Great.",
"options": [
{
"option": 1,
"name": "Grumble Walk",
"text": "Complain loudly but get moving."
},
{
"option": 2,
"name": "Coffee Break",
"text": "Fuel up before facing anyone."
},
{
"option": 3,
"name": "Do Not Disturb",
"text": "Enjoy precious alone time."
},
{
"option": 4,
"name": "Slow March",
"text": "Move at your own (very slow) pace."
}
],
"fun_fact": "Grumpy Cat's real name was Tardar Sauce."
}
}
}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();[
{
"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();{
"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();{
"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": []
}Subscribe to the newsletter / a product.
curl https://api.pocketquant.io/subscribe \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}'import requests
resp = requests.post(
"https://api.pocketquant.io/subscribe",
headers={"x-api-key": API_KEY},
json={'email': 'jane@example.com'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/subscribe", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"email": "jane@example.com"})
});
const data = await resp.json();{
"message": "Subscribed successfully"
}Unsubscribe.
curl https://api.pocketquant.io/unsubscribe \
-X DELETE \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}'import requests
resp = requests.delete(
"https://api.pocketquant.io/unsubscribe",
headers={"x-api-key": API_KEY},
json={'email': 'jane@example.com'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://api.pocketquant.io/unsubscribe", {
method: "DELETE",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"email": "jane@example.com"})
});
const data = await resp.json();{
"message": "Unsubscribed successfully"
}Other
Base URL https://bottlebluellc.com
Submit the website contact form.
curl https://bottlebluellc.com/contact \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Jane", "email": "jane@example.com", "message": "Hi"}'import requests
resp = requests.post(
"https://bottlebluellc.com/contact",
headers={"x-api-key": API_KEY},
json={'name': 'Jane', 'email': 'jane@example.com', 'message': 'Hi'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://bottlebluellc.com/contact", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"name": "Jane", "email": "jane@example.com", "message": "Hi"})
});
const data = await resp.json();{
"message": "Message received!"
}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();{
"status": "ok",
"message": "Endpoint is live"
}Latest sensor readings for the caller (newest first).
| Parameter | In | Description |
|---|---|---|
deviceId | query | optional Restrict to one device serial. |
limit | query | optional Page size (default 10, max 100). |
startKey | query | optional Opaque pagination token from the previous page's nextKey. |
curl https://cloud.bottlebluellc.com/reading \
-H "x-api-key: $BBL_API_KEY"import requests
resp = requests.get(
"https://cloud.bottlebluellc.com/reading",
headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())const resp = await fetch("https://cloud.bottlebluellc.com/reading", {
method: "GET",
headers: { "x-api-key": API_KEY }
});
const data = await resp.json();{
"Items": [
{
"deviceId": "SN-00142",
"ts": 1783503000,
"sensorType": "moisture",
"value": 42.0
}
],
"count": 1,
"deviceId": null,
"nextKey": null
}Subscribe to the newsletter / a product.
curl https://bottlebluellc.com/subscribe \
-X POST \
-H "x-api-key: $BBL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}'import requests
resp = requests.post(
"https://bottlebluellc.com/subscribe",
headers={"x-api-key": API_KEY},
json={'email': 'jane@example.com'},
)
print(resp.status_code, resp.json())const resp = await fetch("https://bottlebluellc.com/subscribe", {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"email": "jane@example.com"})
});
const data = await resp.json();{
"message": "Successfully subscribed!"
}