Bottle Blue LLC

Bottle API Reference

One authenticated REST platform behind several products: financial and economic market data, gardening and environment data, and general-purpose developer utilities.

REST JSON HTTPS v1 · stable
0
Endpoints
across all categories
0
API Hosts
BBL & AquaBotany
0
Categories
data domains
99.9%
Uptime
uptime target

Every request is JSON over HTTPS and authenticated with an API key sent in the x-api-key header. There are two primary hosts:

HostServes
https://api.bottlebluellc.comPlatform: market and government data, utilities, accounts, billing, authentication.
https://api.aquabotany.comAquaBotany: 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.

1

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.

2

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.

3

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);
4

Read the response

A successful call returns 200 OK with a JSON body. Error responses include a requestId, so quote it when contacting support.

200 Response
{
  "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.

StatusMeaning
400Malformed request (bad JSON or parameters).
401Missing or invalid API key.
403Authenticated but not allowed (usually no active subscription).
404No such resource.
429Rate 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.
500Something went wrong on our side. Quote the requestId.
502An upstream data provider returned an error or an unusable response. Code: UPSTREAM_ERROR.
503An upstream data provider is unreachable. Code: UPSTREAM_UNAVAILABLE.
504An 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

ValueMeaning
omitted or emptyYour 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 400 WILDCARD_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, and GET /rewards/history ignore platform. The same goes for GET /images (the presigned upload URL); the platform is recorded when the upload is registered with POST /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
PieceMeaning
bbFixed prefix marking a Bottle Blue reference.
taskWhich building block the record lives in.
defaultWhich platform namespace, the same value you send as platform. See Data platforms.
abc123The 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 + to triple 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 400 WILDCARD_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

POSTapi.bottlebluellc.com/auth/forgot

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();
200 Response
{
  "message": "Password reset code sent successfully"
}
POSTapi.bottlebluellc.com/auth/refresh

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();
200 Response
{
  "access_token": "eyJraWQiOiJ...",
  "id_token": "eyJraWQiOiJ..."
}
POSTapi.bottlebluellc.com/auth/register

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();
200 Response
{
  "message": "User registered successfully"
}
POSTapi.bottlebluellc.com/auth/reset

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();
200 Response
{
  "message": "Password has been reset successfully"
}
POSTapi.bottlebluellc.com/auth/signin

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();
200 Response
{
  "access_token": "eyJraWQiOiJxNFd4MmppcURSQXRZVk5RV1F1VmhBK3M4S0hvSHNpVWFGREFFY3dmeVpZPSIsImFsZyI6IlJTMjU2In0...",
  "id_token": "eyJraWQiOiI2RkRGV2FvMnIyY293aUlxaDlVaVZ6NVg3bCtVRkl6OVU0RVVxNDBZdUU0PSIsImFsZyI6IlJTMjU2In0...",
  "refresh_token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ...",
  "data_platform": "bb_aquabotany",
  "client": "web",
  "token_type": "Bearer",
  "expires_in": 3600
}
POSTapi.bottlebluellc.com/auth/signin-verify

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();
403 Response
{
  "error": "Session verification is currently disabled",
  "code": "FEATURE_DISABLED"
}
POSTapi.bottlebluellc.com/auth/signout

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();
200 Response
{
  "message": "Successfully signed out"
}
POSTapi.bottlebluellc.com/auth/status

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();
200 Response
{
  "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"
    }
  ]
}
POSTapi.bottlebluellc.com/auth/verify

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();
200 Response
{
  "message": "Email verified successfully"
}

API keys

Base URL https://api.bottlebluellc.com

POSTapi.bottlebluellc.com/extend-key

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();
200 Response
{
  "expires_at": 1786113000
}
POSTapi.bottlebluellc.com/generate-key

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();
201 Response
{
  "api_key": "3f9d2c1e-8a4b-4f6e-9c2d-7b1a5e8f0d34",
  "expires_at": 1786113000
}
GETapi.bottlebluellc.com/get-keys

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();
200 Response
[
  {
    "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"
  }
]
POSTapi.bottlebluellc.com/revoke-and-generate-key

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();
403 Response
{
  "error": "Revoke and generate API key is currently disabled",
  "code": "FEATURE_DISABLED"
}
POSTapi.bottlebluellc.com/revoke-key

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();
200 Response
{
  "message": "API key revoked successfully",
  "updatedAttributes": {
    "key_status": "revoked"
  }
}
POSTapi.bottlebluellc.com/validate-key

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();
403 Response
{
  "error": "API access is currently disabled",
  "code": "FEATURE_DISABLED"
}

Subscriptions & billing

Base URL https://api.bottlebluellc.com

POSTapi.bottlebluellc.com/create-checkout-session

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();
200 Response
{
  "id": "cs_live_a1B2c3D4e5F6g7H8i9J0kLmNoPqRsTuVwXyZ0a1b2c3d4e5f6g7h8"
}
POSTapi.bottlebluellc.com/create-portal-session

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();
200 Response
{
  "url": "https://billing.stripe.com/p/session/live_a1B2c3D4e5F6g7H8"
}
GETapi.bottlebluellc.com/get-subscription-plans

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();
200 Response
[
  {
    "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"
  }
]
GETapi.bottlebluellc.com/get-subscription-status

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();
200 Response
{
  "email": "you@example.com",
  "auto_renew": true,
  "renew_date": "2026-08-13",
  "levels": [
    "Pro",
    "Free"
  ]
}
POSTapi.bottlebluellc.com/get-usage-stats

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();
200 Response
{
  "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
  }
}
POSTapi.bottlebluellc.com/stripe/webhook

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();
200 Response
Subscription status updated
POSTapi.bottlebluellc.com/subscription-webhook

Subscription 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();
200 Response
Subscription status updated

Market data

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/market

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();
200 Response
{
  "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" }
  }
}
GETapi.bottlebluellc.com/market/news

Stored market headlines.

ParameterInDescription
sourcequeryoptional Filter by source.
limitqueryoptional 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();
200 Response
{
  "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"
    }
  ]
}
GETapi.bottlebluellc.com/market/predictions

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();
200 Response
{
  "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."
    }
  ]
}
GETapi.bottlebluellc.com/market/{category}

Latest daily snapshot for a market category.

ParameterInDescription
categorypathrequired 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.
datequeryoptional 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();
200 Response
{
  "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"
    }
  ]
}
GETapi.bottlebluellc.com/market/{category}/{entity}

Historical time series for one instrument or series.

ParameterInDescription
categorypathrequired A market category (see /market).
entitypathrequired Instrument/series id, e.g. ^TNX, BTC-USD, DFF.
startqueryoptional YYYY-MM-DD start of range.
endqueryoptional 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();
200 Response
{
  "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"
    }
  ]
}
GETapi.bottlebluellc.com/market/earnings

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.

ParameterInDescription
daysqueryoptional Forward window in days (default 8, max 40).
datequeryoptional 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();
200 Response
{
  "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"
    }
  ]
}
GETapi.bottlebluellc.com/market/stats

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.

ParameterInDescription
categorypathoptional A market slug (bonds, commodities, crypto, ...). Omit for every instrument.
entitypathoptional An instrument id under that category, e.g. ^TNX.
datequeryoptional 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();
200 Response
{
  "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

GETapi.bottlebluellc.com/government/{dataset}

US government financial-disclosure datasets.

ParameterInDescription
datasetpathrequired congress, senate, house, lobbying, contracts, off-exchange, insiders, wsb, or 13f-changes.
yearqueryoptional 4-digit year (default current).
limitqueryoptional 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();
200 Response
{
  "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

POSTapi.bottlebluellc.com/math

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();
200 Response
{
  "result": 1628.89,
  "meta": {
    "category": "finance",
    "formula_id": "finance:compound_interest"
  }
}
GETapi.bottlebluellc.com/ntp

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();
200 Response
{
  "utc_iso": "2026-07-07T12:34:56.789Z",
  "epoch_s": 1752237296.789,
  "epoch_ms": 1752237296789,
  "epoch_ns": 1752237296789000000,
  "lambda_request_id": "abc123-def456"
}
GETapi.bottlebluellc.com/rng

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.

ParameterInDescription
formatqueryoptional 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();
200 Response
{
  "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"
}
GETapi.bottlebluellc.com/poetry

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();
200 Response
{
  "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
  }
}
GETapi.bottlebluellc.com/seconds/midnight

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();
200 Response
{
  "timezone": "UTC",
  "remainingMilliseconds": 16070000
}
GETapi.bottlebluellc.com/seconds/season

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();
200 Response
{
  "date": "2026-07-08",
  "season": "Summer"
}
GETapi.bottlebluellc.com/seconds/sunset

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();
200 Response
{
  "millisecondsUntilSunset": 23390000,
  "current_time": "2026-07-08 14:32:10",
  "sunset_time": "2026-07-08 21:02:00"
}
POSTapi.bottlebluellc.com/units

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).

ParameterInDescription
valuebodyrequired Numeric value to convert.
frombodyrequired Source unit code, e.g. km.
tobodyrequired 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();
200 Response
{
  "input": {
    "value": 100,
    "from": "km",
    "to": "mi"
  },
  "result": 62.137273664980675,
  "formula": "100 * 1000.0 / 1609.34"
}

Notes

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/notes

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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.bottlebluellc.com/notes

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"})
});
200 Response
{
  "message": "Note created",
  "note_id": "9b1c2f6e-4a3d-4c8b-b2e1-7f0d9a5c3e21"
}
GETapi.bottlebluellc.com/notes/{note_id}

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 }
});
200 Response
{
  "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"
}
PUTapi.bottlebluellc.com/notes/{note_id}

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"})
});
200 Response
{
  "message": "Note updated"
}
PATCHapi.bottlebluellc.com/notes/{note_id}

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"})
});
200 Response
{
  "message": "Note updated"
}
DELETEapi.bottlebluellc.com/notes/{note_id}

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 }
});
200 Response
{
  "message": "Note deleted"
}
POSTapi.bottlebluellc.com/links

Link two records together. Also available on api.aquabotany.com.

ParameterInDescription
frombodyrequired Ref of the record the link starts at.
tobodyrequired Ref of the record the link points at.
typebodyoptional Relationship verb, your own vocabulary (letters, digits, and _.- , max 64). Defaults to related.
platformbodyoptional 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"
  })
});
200 Response
{
  "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"
  }
}

Lists

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/list

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.bottlebluellc.com/list

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"})
});
200 Response
{
  "status": "created",
  "list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
  "name": "Shopping"
}
GETapi.bottlebluellc.com/list/{listId}

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 }
});
200 Response
{
  "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"
    }
  ]
}
DELETEapi.bottlebluellc.com/list/{listId}

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 }
});
200 Response
{
  "status": "list deleted",
  "list_id": "3f2c9a71-8b4e-4d2a-9c6e-1f5a7b3d8e42",
  "deleted": 2
}
POSTapi.bottlebluellc.com/list/{listId}/item

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"})
});
200 Response
{
  "status": "item added",
  "item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}
PUTapi.bottlebluellc.com/list/{listId}/item/{itemId}

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"})
});
200 Response
{
  "status": "updated",
  "item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}
PATCHapi.bottlebluellc.com/list/{listId}/item/{itemId}

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"})
});
200 Response
{
  "status": "updated",
  "item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}
DELETEapi.bottlebluellc.com/list/{listId}/item/{itemId}

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 }
});
200 Response
{
  "status": "deleted",
  "item_id": "c4a8f1d2-6e3b-4c97-a5d8-0b2f9e7c4a13"
}

Queues

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/queue/{queueId}

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 }
});
200 Response
{
  "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"
    }
  ]
}
DELETEapi.bottlebluellc.com/queue/{queueId}

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 }
});
200 Response
{
  "status": "purged",
  "queue_id": "QUEUE_ID",
  "deleted": 2
}
POSTapi.bottlebluellc.com/queue/{queueId}/claim

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"})
});
200 Response
{
  "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"
}
POSTapi.bottlebluellc.com/queue/{queueId}/join

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"})
});
200 Response
{
  "status": "enqueued",
  "queue_id": "QUEUE_ID",
  "item_id": "0b6f1a3e-9d2c-4f5a-8e71-2c9d4b6a1f30"
}
GETapi.bottlebluellc.com/queue/{queueId}/next

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 }
});
200 Response
{
  "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
  }
}
POSTapi.bottlebluellc.com/queue/{queueId}/renew

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"})
});
200 Response
{
  "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"
  }
}
DELETEapi.bottlebluellc.com/queue/{queueId}/{itemId}

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 }
});
200 Response
{
  "status": "deleted",
  "forced": false,
  "queue_id": "QUEUE_ID",
  "item_id": "ITEM_ID"
}
POSTapi.bottlebluellc.com/queue/{queueId}/{itemId}/ack

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"})
});
200 Response
{
  "status": "acknowledged",
  "queue_id": "QUEUE_ID",
  "item_id": "ITEM_ID"
}

Streaks

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/streaks/{userId}

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 }
});
200 Response
{
  "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"
}
PUTapi.bottlebluellc.com/streaks/{userId}

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"})
});
200 Response
{
  "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"
}
PATCHapi.bottlebluellc.com/streaks/{userId}

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"})
});
200 Response
{
  "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"
}
DELETEapi.bottlebluellc.com/streaks/{userId}

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 }
});
200 Response
{
  "status": "cleared",
  "user_id": "USER_ID",
  "deleted": 31
}
POSTapi.bottlebluellc.com/streaks/{userId}/checkin

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"})
});
200 Response
{
  "status": "ok",
  "user_id": "USER_ID",
  "date": "2026-07-08",
  "current_streak": 6,
  "best_streak": 12,
  "last_active_date": "2026-07-08",
  "version": 18
}
GETapi.bottlebluellc.com/streaks/{userId}/history

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 }
});
200 Response
{
  "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

POSTapi.bottlebluellc.com/leaderboard/score

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"})
});
200 Response
{
  "message": "Score submitted",
  "user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
  "score": 4200
}
GETapi.bottlebluellc.com/leaderboard/top

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 }
});
200 Response
[
  {
    "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"
  }
]
GETapi.bottlebluellc.com/leaderboard/{userId}

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 }
});
200 Response
{
  "user_id": "u_9f3a1c7b8e2d0a4f", "isYou": true,
  "score": 4200,
  "updated_at": "2026-07-08T14:07:45.220145"
}
DELETEapi.bottlebluellc.com/leaderboard/{userId}

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 }
});
200 Response
{
  "message": "Score deleted for you@example.com"
}

Scoreboards

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/scoreboard/{matchId}

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 }
});
200 Response
{
  "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"
    }
  ]
}
DELETEapi.bottlebluellc.com/scoreboard/{matchId}

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 }
});
200 Response
{
  "status": "cleared",
  "match_id": "MATCH_ID",
  "deleted": 2
}
PUTapi.bottlebluellc.com/scoreboard/{matchId}/team/{teamId}

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"})
});
200 Response
{
  "status": "ok",
  "mode": "set",
  "match_id": "MATCH_ID",
  "team_id": "TEAM_ID",
  "score": 21
}
PATCHapi.bottlebluellc.com/scoreboard/{matchId}/team/{teamId}

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"})
});
200 Response
{
  "status": "ok",
  "mode": "delta",
  "match_id": "MATCH_ID",
  "team_id": "TEAM_ID",
  "score": 24
}
POSTapi.bottlebluellc.com/scoreboard/{matchId}/team/{teamId}/increment

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"})
});
200 Response
{
  "status": "ok",
  "match_id": "MATCH_ID",
  "team_id": "TEAM_ID",
  "score": 25
}

Assets

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/assets

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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.bottlebluellc.com/assets

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"})
});
201 Response
{
  "assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07"
}
GETapi.bottlebluellc.com/assets/{id}

Get a single asset.

ParameterInDescription
idpathrequired 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 }
});
200 Response
{
  "entityType": "asset",
  "type": "structure",
  "assetType": "structure",
  "name": "Garden shed",
  "createdAt": "2026-07-08T09:30:12Z",
  "metadata": {
    "color": "green"
  },
  "assetId": "0c9f4e2a-7d61-4b83-9e5a-3f8b1c6d2e07"
}
PATCHapi.bottlebluellc.com/assets/{id}

Merge fields into an asset's metadata.

ParameterInDescription
idpathrequired 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"})
});
200 Response
{
  "message": "Asset updated"
}
DELETEapi.bottlebluellc.com/assets/{id}

Delete an asset.

ParameterInDescription
idpathrequired 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 }
});
200 Response
{
  "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.

GETapi.bottlebluellc.com/calculate

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();
200 Response
{
  "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"] }
}
GETapi.bottlebluellc.com/finance/calculate/mortgage?principal=300000&annual_rate_pct=6.5&years=30

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();
200 Response
{
  "domain": "finance",
  "type": "mortgage",
  "result": {
    "monthly_payment": 1896.2,
    "number_of_payments": 360,
    "total_paid": 682633.47,
    "total_interest": 382633.47
  }
}
GETapi.bottlebluellc.com/fitness/calculate/tdee?weight_kg=80&height_cm=180&age=30&sex=male&activity=moderate

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();
200 Response
{
  "domain": "fitness",
  "type": "tdee",
  "result": {
    "bmr_kcal_per_day": 1780,
    "tdee_kcal_per_day": 2759,
    "activity_multiplier": 1.55
  }
}
GETapi.bottlebluellc.com/realestate/calculate/cap-rate?property_value=400000&noi=24000

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();
200 Response
{
  "domain": "realestate",
  "type": "cap-rate",
  "result": { "cap_rate_pct": 6.0, "noi": 24000.0 }
}

Market analysis

Base URL https://api.bottlebluellc.com

GETapi.bottlebluellc.com/market/econ-summary

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();
200 Response
{
  "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"
}
GETapi.bottlebluellc.com/market/technicals/^GSPC

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();
200 Response
{
  "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

  1. A role is a set of permission strings shaped resource:action, for example notes:read, notes:write, or pages/dashboard:read.
  2. Wildcards are supported: * or *:* grant everything, resource:* grants every action on a resource, and *:action grants an action across resources.
  3. A member is assigned one or more roles; their effective permissions are the union of those roles.
  4. 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

GETapi.bottlebluellc.com/access/roles

List the roles defined for a product platform. Operator-only (x-admin-key).

ParameterInDescription
platformqueryRequired. 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();
200 Response
{
  "platform": "100000000000001",
  "roles": [
    { "role": "editor", "permissions": ["notes:*", "pages/dashboard:read"], "updatedAt": "2026-07-17T14:00:00+00:00" }
  ]
}
POSTapi.bottlebluellc.com/access/roles

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();
200 Response
{
  "platform": "100000000000001",
  "role": "editor",
  "permissions": ["notes:*", "pages/dashboard:read"],
  "updatedAt": "2026-07-17T14:00:00+00:00"
}
DELETEapi.bottlebluellc.com/access/roles

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();
200 Response
{
  "platform": "100000000000001",
  "role": "editor",
  "deleted": true
}
GETapi.bottlebluellc.com/access/members

List members of a product platform and the roles assigned to each. Operator-only (x-admin-key).

ParameterInDescription
platformqueryRequired. 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();
200 Response
{
  "platform": "100000000000001",
  "members": [
    { "email": "user@example.com", "roles": ["editor"], "updatedAt": "2026-07-17T14:00:00+00:00" }
  ]
}
POSTapi.bottlebluellc.com/access/members

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();
200 Response
{
  "platform": "100000000000001",
  "email": "user@example.com",
  "roles": ["editor"],
  "updatedAt": "2026-07-17T14:00:00+00:00"
}
DELETEapi.bottlebluellc.com/access/members

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();
200 Response
{
  "platform": "100000000000001",
  "email": "user@example.com",
  "deleted": true
}
GETapi.bottlebluellc.com/access/check

Test whether a member holds a permission (wildcard-aware). Operator-only (x-admin-key).

ParameterInDescription
platformqueryRequired. A registered Bottle Blue product platform id.
emailqueryRequired. The member to test.
permissionqueryRequired. 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();
200 Response
{
  "platform": "100000000000001",
  "email": "user@example.com",
  "permission": "pages/dashboard:read",
  "allow": true
}

First-party app authentication (user password → Bearer token)

Note for first-party apps: The 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:

  1. x-api-key header - looks up the key, resolves the owner email, checks status/expiry.
  2. 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:

ValueProduct
bb_aquabotanyAquaBotany
bb_bottleblueBottle Blue App
bb_pocketquantPocket Quant
bb_apparatusApparatus Ready
bb_tracklessTrackless

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

ScenarioUseWhy
End-user product (customer of the platform)x-api-keyPurchased 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 yourselfBearer token from a fresh sign-inFull access, tied to your Cognito account, no subscription billing
Security note: Never embed a raw password or long-lived API key in client-side code you ship. For server-side scripts and AI agents, sign in programmatically at startup, cache the access token for its TTL, then refresh. The refresh_token itself should be stored securely (env var / secrets manager) and never logged.
GETapi.bottlebluellc.com/admin/get-all-keys

(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();
200 Response
{
  "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"
    }
  ]
}
GETapi.bottlebluellc.com/admin/get-logs

(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();
200 Response
{
  "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
    }
  ]
}
POSTapi.bottlebluellc.com/admin/message/email

(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();
200 Response
{
  "message": "Email sent successfully",
  "messageId": "0100019789a4b2c3-7f3e5d1a-42b6-4c8f-9a0d-6e2b8c4f1d35-000000"
}
POSTapi.bottlebluellc.com/admin/message/sms

(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();
501 Response
{
  "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."
}
GETapi.bottlebluellc.com/admin/metrics

(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();
200 Response
{
  "metrics": {
    "total_requests": 18432,
    "unique_ips": 76,
    "endpoints": [
      "/market",
      "/weather",
      "/ntp",
      "/notes",
      "/validate-key"
    ]
  }
}

Weather

Base URL https://api.aquabotany.com

GETapi.aquabotany.com/weather

Current conditions and forecast for a coordinate.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/weather/alerts

Active NWS watches, warnings, and advisories covering a point.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/weather/forecast

Detailed multi-day (or hourly) NWS forecast for a coordinate.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
hourlyqueryoptional '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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/weather/history

Daily historical weather over a date range (ERA5 reanalysis).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
start_datequeryrequired YYYY-MM-DD.
end_datequeryrequired 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();
200 Response
{
  "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)"
}
GETapi.aquabotany.com/weather/product

The latest NWS text product (forecast discussion, roundup, etc.).

ParameterInDescription
typequeryoptional Product code (default AFD). e.g. RWR, HWO.
locationqueryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/weather/seasonal-advice

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();
200 Response
{
  "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."
  ]
}
GETapi.aquabotany.com/weather/zone

Look up the hardiness zone by lat/lon query params.

ParameterInDescription
latqueryoptional GET: latitude (with lon).
lonqueryoptional 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();
200 Response
{
  "zone": "4",
  "latitude": 44.98,
  "longitude": -93.26
}
POSTapi.aquabotany.com/weather/zone

Look up the hardiness zone by JSON body (zip or lat/lon).

ParameterInDescription
latqueryoptional GET: latitude (with lon).
lonqueryoptional 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();
200 Response
{
  "zone": "4",
  "latitude": 44.979265,
  "longitude": -93.273024
}

Environment

Base URL https://api.aquabotany.com

GETapi.aquabotany.com/environment/air

Current air quality for a coordinate (US coverage).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
distancequerySearch 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/air/forecast

Daily air-quality outlook, global: mean pollutant levels and peak AQI per day.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
daysqueryoptional 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();
200 Response
{
  "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"
  }
}
GETapi.aquabotany.com/environment/air/history

Daily air-quality history, global (up to 92 days back).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
daysqueryoptional 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();
200 Response
{
  "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"
  }
}
GETapi.aquabotany.com/environment/earthquakes

Recent earthquakes, optionally near a coordinate.

ParameterInDescription
latqueryoptional Latitude (filter near a point).
lonqueryoptional Longitude.
radius_kmqueryoptional Search radius (default 500).
min_magnitudequeryoptional Minimum magnitude.
windowqueryoptional '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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/elevation

Terrain elevation at a coordinate.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "location": {
    "lat": 44.98,
    "lon": -93.26
  },
  "elevation_m": 254.0,
  "source": "open-meteo"
}
GETapi.aquabotany.com/environment/elevation-by-place

Terrain elevation for a named place.

ParameterInDescription
namequeryrequired 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();
200 Response
{
  "query": "Kyoto",
  "resolved": {
    "name": "Kyoto",
    "lat": 35.02107,
    "lon": 135.75385,
    "country": "Japan",
    "admin1": "Kyōto"
  },
  "elevation_m": 45.0,
  "source": "open-meteo"
}
GETapi.aquabotany.com/environment/events

Active natural-hazard events across all categories (storms, floods, volcanoes, ...).

ParameterInDescription
categoryqueryoptional EONET category id (e.g. severeStorms, volcanoes, floods); omit for all categories.
latqueryoptional Latitude (filter near a point).
lonqueryoptional Longitude.
radius_kmqueryoptional Search radius (default 200).
limitqueryoptional 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();
200 Response
{
  "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
}
GETapi.aquabotany.com/environment/flora-and-fauna

Nearby species observations.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
radiusquerySearch 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();
200 Response
{
  "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"
    }
  ]
}
GETapi.aquabotany.com/environment/moon

Lunar phase for a date.

ParameterInDescription
datequeryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/pollen

Current airborne pollen levels by type (Europe coverage).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/pollen/forecast

Daily pollen outlook: each type's peak level with a severity band (Europe coverage).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
daysqueryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/pollen/history

Daily pollen history, up to 92 days back (Europe coverage).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
daysqueryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/soil/composition

Soil classification (WRB) for a coordinate.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/soil/moisture

Live soil temperature and moisture at several depths.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/environment/soil/properties

Numeric soil properties (pH, organic carbon, ...).

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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
      }
    }
  }
}
GETapi.aquabotany.com/environment/wildfire

Active wildfire events, optionally near a coordinate.

ParameterInDescription
latqueryoptional Latitude (filter near a point).
lonqueryoptional Longitude.
radius_kmqueryoptional Search radius (default 200).
limitqueryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/sustainability/recycling-locations

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();
200 Response
{
  "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."
}
GETapi.aquabotany.com/sustainability/tips

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();
200 Response
{
  "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

GETapi.aquabotany.com/location/geocode

Resolve a place name to coordinates.

ParameterInDescription
namequeryrequired Town, city, or landmark.
countqueryoptional 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/location/reverse-geocode

Resolve a coordinate to the nearest address.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired 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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/places/kwik-trip

Nearest Kwik Trip / Kwik Star stores to a coordinate.

ParameterInDescription
latqueryrequired Latitude.
lonqueryrequired Longitude.
radius_kmqueryoptional Only stores within this radius.
limitqueryoptional 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();
200 Response
{
  "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

GETapi.aquabotany.com/my-plants

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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.aquabotany.com/my-plants

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"})
});
201 Response
{
  "message": "Plant added successfully",
  "plant_id": "3f9c2a71-8e4d-4c2b-9f6a-2d7e0b5a1c34"
}
PUTapi.aquabotany.com/my-plants/{plantId}

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"})
});
200 Response
{
  "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"
  }
}
DELETEapi.aquabotany.com/my-plants/{plantId}

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 }
});
200 Response
{
  "message": "Plant deleted successfully"
}
GETapi.aquabotany.com/plants/plantfinder

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();
200 Response
[
  {
    "CommonName": "Bee Balm",
    "Zone": "4"
  },
  {
    "CommonName": "Purple Coneflower",
    "Zone": "4"
  }
]
GETapi.aquabotany.com/plants/tips

Plant-care tips.

ParameterInDescription
plantqueryrequired 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();
200 Response
{
  "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

GETapi.aquabotany.com/automation-devices

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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.aquabotany.com/automation-devices

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"})
});
201 Response
{
  "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"
}
PUTapi.aquabotany.com/automation-devices/{deviceId}

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"})
});
200 Response
{
  "message": "Device updated successfully"
}
DELETEapi.aquabotany.com/automation-devices/{deviceId}

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 }
});
200 Response
{
  "message": "Device deleted successfully"
}
GETapi.aquabotany.com/devices/{deviceId}/commands

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/devices/{deviceId}/commands

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"})
});
200 Response
{
  "message": "Command created",
  "commandId": "7f3a9c21-64bd-4e5a-8f1c-0d2b6e9a3c58"
}
DELETEapi.aquabotany.com/devices/{deviceId}/commands/{commandId}

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 }
});
200 Response
{
  "message": "Command deleted"
}
GETapi.aquabotany.com/garden-environment

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/garden-environment

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"})
});
200 Response
{
  "message": "Environment added successfully",
  "environment_id": "c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47"
}
PUTapi.aquabotany.com/garden-environment/{environmentId}

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"})
});
200 Response
{
  "message": "Environment updated successfully",
  "updated_attributes": {
    "Name": "Backyard bed",
    "Details": {
      "zone": "5a"
    },
    "UpdatedAt": "2026-07-08T10:02:11.907414+00:00"
  }
}
DELETEapi.aquabotany.com/garden-environment/{environmentId}

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 }
});
200 Response
{
  "message": "Environment with ID c2a8f1d3-7b64-4e09-a5d2-8f3c1b6e9a47 deleted successfully"
}
GETapi.aquabotany.com/sensor-data

Submit or read garden sensor readings.

ParameterInDescription
deviceIdqueryoptional Restrict to one device serial.
startKeyqueryoptional 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();
200 Response
{
  "Items": [
    {
      "deviceId": "SN-00142",
      "ts": 1783503000,
      "sensorType": "moisture",
      "value": 42.0
    },
    {
      "deviceId": "SN-00142",
      "ts": 1783499400,
      "sensorType": "temp_c",
      "value": 21.5
    }
  ],
  "nextKey": null
}
POSTapi.aquabotany.com/sensor-data

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();
202 Response
{
  "msg": "Accepted 2 readings 🌱"
}

Tasks & reminders

Base URL https://api.aquabotany.com

GETapi.aquabotany.com/reminders

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/reminders

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"})
});
201 Response
{
  "reminderId": "9f2c4b7d8a1e4f60b3c5d7e9a2f14c86"
}
GETapi.aquabotany.com/reminders/{id}

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 }
});
200 Response
{
  "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"
}
PUTapi.aquabotany.com/reminders/{id}

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"})
});
200 Response
{
  "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"
}
DELETEapi.aquabotany.com/reminders/{id}

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 }
});
204 Response
GETapi.aquabotany.com/tasks

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 }
});
200 Response
[
  {
    "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
  }
]
POSTapi.aquabotany.com/tasks

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"})
});
200 Response
{
  "message": "Task created successfully",
  "task_id": "d1a5f8e2-3c7b-4a90-b6d4-8e2f5c1a7b39"
}
POSTapi.aquabotany.com/tasks/completed

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"})
});
200 Response
{
  "status": "completed"
}
GETapi.aquabotany.com/tasks/reminders

List the caller's reminders, soonest-due first.

ParameterInDescription
statusqueryoptional GET filter: scheduled|done|cancelled.
limitqueryoptional GET: max results (default 50, max 200).
idqueryoptional 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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.aquabotany.com/tasks/reminders

Create a reminder.

ParameterInDescription
statusqueryoptional GET filter: scheduled|done|cancelled.
limitqueryoptional GET: max results (default 50, max 200).
idqueryoptional 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"})
});
201 Response
{
  "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"
  }
}
PATCHapi.aquabotany.com/tasks/reminders

Update a reminder (including marking it done).

ParameterInDescription
statusqueryoptional GET filter: scheduled|done|cancelled.
limitqueryoptional GET: max results (default 50, max 200).
idqueryoptional 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"})
});
200 Response
{
  "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"
  }
}
DELETEapi.aquabotany.com/tasks/reminders

Delete a reminder (by ?id=).

ParameterInDescription
statusqueryoptional GET filter: scheduled|done|cancelled.
limitqueryoptional GET: max results (default 50, max 200).
idqueryoptional 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 }
});
200 Response
{
  "deleted": "5b8e2d4a-7c31-49f6-a2d8-0e6b3f9c1a57"
}
GETapi.aquabotany.com/tasks/scheduled

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 }
});
200 Response
[
  {
    "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
  }
]
GETapi.aquabotany.com/tasks/{task_id}

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 }
});
200 Response
{
  "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
}
PUTapi.aquabotany.com/tasks/{task_id}

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"})
});
200 Response
{
  "status": "updated"
}
PATCHapi.aquabotany.com/tasks/{task_id}

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"})
});
200 Response
{
  "status": "updated"
}
DELETEapi.aquabotany.com/tasks/{task_id}

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 }
});
200 Response
{
  "status": "deleted"
}

Community

Base URL https://api.aquabotany.com

GETapi.aquabotany.com/articles

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 }
});
200 Response
{
  "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
}
POSTapi.aquabotany.com/articles

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"})
});
201 Response
{
  "articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83"
}
GETapi.aquabotany.com/articles/{articleId}

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 }
});
200 Response
{
  "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
}
PUTapi.aquabotany.com/articles/{articleId}

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"})
});
200 Response
{
  "message": "updated"
}
PATCHapi.aquabotany.com/articles/{articleId}

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"})
});
200 Response
{
  "message": "updated"
}
DELETEapi.aquabotany.com/articles/{articleId}

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 }
});
204 Response
{}
GETapi.aquabotany.com/articles/{articleId}/comments

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 }
});
200 Response
{
  "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
}
POSTapi.aquabotany.com/articles/{articleId}/comments

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"})
});
201 Response
{
  "commentId": "5f8e2b1a-7c4d-4b9e-8a3f-6d1c0e9b5a27"
}
PATCHapi.aquabotany.com/articles/{articleId}/comments/{commentId}

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"})
});
200 Response
{
  "message": "updated"
}
DELETEapi.aquabotany.com/articles/{articleId}/comments/{commentId}

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 }
});
204 Response
{}
GETapi.aquabotany.com/articles/{articleId}/likes

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 }
});
200 Response
{
  "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
}
POSTapi.aquabotany.com/articles/{articleId}/likes

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"})
});
201 Response
{}
DELETEapi.aquabotany.com/articles/{articleId}/likes

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 }
});
204 Response
{}
GETapi.aquabotany.com/articles/{articleId}/shares

Shares of an article.

# Default namespace (platform omitted)
curl https://api.aquabotany.com/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares \
  -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/shares?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/shares",
    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/shares",
    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/shares", {
  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/shares?platform=my_garden", {
  method: "GET",
  headers: { "x-api-key": API_KEY }
});
200 Response
{
  "items": [
    {
      "userId": "u_9f3a1c7b8e2d0a4f", "isYou": true,
      "articleId": "e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83",
      "sharedAt": "2026-07-08T14:35:10Z"
    }
  ]
}
POSTapi.aquabotany.com/articles/{articleId}/shares

Shares of an article.

# Default namespace (platform omitted)
curl https://api.aquabotany.com/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares \
  -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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares?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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares",
    headers={"x-api-key": API_KEY},
)
print(resp.status_code, resp.json())

# Scoped to one platform
resp = requests.post(
    "https://api.aquabotany.com/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares",
    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/shares", {
  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/articles/e2a9c4d7-8b3f-4e1a-a6c5-9d0f2b7e4a83/shares?platform=my_garden", {
  method: "POST",
  headers: { "x-api-key": API_KEY }
});
201 Response
{}
GETapi.aquabotany.com/articles/{articleId}/stats

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 }
});
200 Response
{
  "likeCount": 3,
  "shareCount": 1,
  "commentCount": 2
}
GETapi.aquabotany.com/comments

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 }
});
200 Response
{
  "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
}
GETapi.aquabotany.com/feed

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 }
});
200 Response
{
  "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
}
GETapi.aquabotany.com/likes

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 }
});
200 Response
{
  "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

Catalog write permissions (events, challenges, rewards). These three catalogs are shared per 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.
GETapi.aquabotany.com/actions

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 }
});
200 Response
{
  "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
}
POSTapi.aquabotany.com/actions

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"})
});
201 Response
{
  "message": "Action logged",
  "actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02"
}
GETapi.aquabotany.com/actions/user-progress

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 }
});
200 Response
{
  "totalActions": 2,
  "action_counts": {
    "watering": 1,
    "composting": 1
  }
}
GETapi.aquabotany.com/actions/{actionId}

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 }
});
200 Response
{
  "userId": "coughlin058@gmail.com",
  "actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
  "timestamp": "2026-07-08T09:14:23.481932",
  "actionType": "watering",
  "description": "Watered garden",
  "metadata": {}
}
PUTapi.aquabotany.com/actions/{actionId}

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"})
});
200 Response
{
  "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": {}
}
PATCHapi.aquabotany.com/actions/{actionId}

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"})
});
200 Response
{
  "userId": "coughlin058@gmail.com",
  "actionId": "8f3d2c1a-7b4e-4e2a-9c6d-1a5b3e7f9d02",
  "timestamp": "2026-07-08T09:14:23.481932",
  "actionType": "pruning",
  "description": "Watered garden",
  "metadata": {}
}
DELETEapi.aquabotany.com/actions/{actionId}

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 }
});
200 Response
{
  "status": "deleted"
}
GETapi.aquabotany.com/challenges

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/challenges

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"})
});
201 Response
{
  "message": "Challenge created",
  "challengeId": "2e7a9c4d-5b1f-4a8e-bd63-0c9e2f7a4b15"
}
GETapi.aquabotany.com/challenges/active

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 }
});
200 Response
[
  {
    "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"
  }
]
GETapi.aquabotany.com/challenges/history

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 }
});
200 Response
[
  {
    "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"
  }
]
GETapi.aquabotany.com/challenges/{challengeId}

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 }
});
200 Response
{
  "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"
}
PUTapi.aquabotany.com/challenges/{challengeId}

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"})
});
200 Response
{
  "message": "Challenge updated"
}
DELETEapi.aquabotany.com/challenges/{challengeId}

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 }
});
204 Response
POSTapi.aquabotany.com/challenges/{challengeId}/complete

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 }
});
200 Response
{
  "message": "Challenge marked as completed"
}
GETapi.aquabotany.com/events

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 }
});
200 Response
{
  "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"
    }
  ]
}
POSTapi.aquabotany.com/events

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"})
});
201 Response
{
  "eventId": "a3f8c2d1-7b4e-4c9a-9e2f-5d8b6a1c0e73"
}
GETapi.aquabotany.com/events/upcoming

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 }
});
200 Response
{
  "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"
    }
  ]
}
GETapi.aquabotany.com/events/{eventId}

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 }
});
200 Response
{
  "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"
}
PUTapi.aquabotany.com/events/{eventId}

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"})
});
200 Response
{
  "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"
}
PATCHapi.aquabotany.com/events/{eventId}

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"})
});
200 Response
{
  "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"
}
DELETEapi.aquabotany.com/events/{eventId}

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 }
});
200 Response
{
  "status": "deleted"
}
GETapi.aquabotany.com/rewards

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 }
});
200 Response
[
  {
    "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."
  }
]
POSTapi.aquabotany.com/rewards

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"})
});
201 Response
{
  "rewardId": "c7d2a940-8f1b-4e6c-a3d5-2b9e0f7c4a61"
}
GETapi.aquabotany.com/rewards/history

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();
200 Response
[
  {
    "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"
  }
]
GETapi.aquabotany.com/rewards/points

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();
200 Response
{
  "points": 175
}
POSTapi.aquabotany.com/rewards/points/add

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();
200 Response
{
  "points": 225
}
POSTapi.aquabotany.com/rewards/redeem

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"})
});
200 Response
{
  "message": "Reward redeemed successfully",
  "remainingPoints": 125
}
GETapi.aquabotany.com/rewards/{rewardId}

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 }
});
200 Response
{
  "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"
}
PUTapi.aquabotany.com/rewards/{rewardId}

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"})
});
200 Response
{
  "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"
}
DELETEapi.aquabotany.com/rewards/{rewardId}

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 }
});
204 Response

Notifications

Base URL https://api.aquabotany.com

POSTapi.aquabotany.com/notifications

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"})
});
201 Response
{
  "message": "Notification created",
  "notifId": "9f3c1a2b4d5e6f708192a3b4c5d6e7f8"
}
GETapi.aquabotany.com/notifications/{userId}

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 }
});
200 Response
[
  {
    "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"
  }
]
PUTapi.aquabotany.com/notifications/{userId}

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"})
});
400 Response
{
  "message": "Unsupported route"
}
POSTapi.aquabotany.com/notifications/{userId}/read-all

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 }
});
200 Response
{
  "message": "Marked 2 notifications as read."
}
PUTapi.aquabotany.com/notifications/{userId}/{notifId}

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"})
});
200 Response
{
  "notifId": "9f3c1a2b4d5e6f708192a3b4c5d6e7f8",
  "title": "Time to water",
  "body": "Your basil needs water",
  "status": "read",
  "createdAt": "2026-07-08T14:12:31.482913"
}
DELETEapi.aquabotany.com/notifications/{userId}/{notifId}

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 }
});
204 Response

Content & settings

Base URL https://api.aquabotany.com

POSTapi.aquabotany.com/ai/requests

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();
202 Response
{
  "requestId": "b3f8c2d1-4e5a-4b6c-9d7e-1f2a3b4c5d6e"
}
GETapi.aquabotany.com/ai/requests/{requestId}

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();
200 Response
{
  "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"
}
GETapi.aquabotany.com/images

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();
200 Response
{
  "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
}
POSTapi.aquabotany.com/images

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"})
});
201 Response
{
  "message": "Image metadata saved",
  "imageId": "7c2e9d84-51a3-4f6b-8e2d-0a9b3c4d5e6f"
}
GETapi.aquabotany.com/learning-resources

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();
200 Response
[
  {
    "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/"
  }
]
GETapi.aquabotany.com/marketplace

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();
200 Response
{
  "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
    }
  ]
}
GETapi.aquabotany.com/settings

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/settings

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"})
});
201 Response
{
  "message": "Setting saved"
}
GETapi.aquabotany.com/settings/{settingName}

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 }
});
200 Response
{
  "value": {
    "system": "metric"
  },
  "lastModified": "2026-07-08T14:15:09.271836",
  "settingName": "units"
}
DELETEapi.aquabotany.com/settings/{settingName}

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 }
});
204 Response

AquaBotany account

Base URL https://api.aquabotany.com

GETapi.aquabotany.com/dashboard

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();
200 Response
[
  {
    "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
          }
        ]
      }
    }
  }
]
GETapi.aquabotany.com/subscription

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();
200 Response
{
  "email": "you@example.com",
  "plan_id": "plan_pro",
  "start_date": "2026-07-08T14:16:44.108829",
  "status": "active"
}
POSTapi.aquabotany.com/subscription

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();
201 Response
{
  "email": "you@example.com",
  "plan_id": "plan_pro",
  "start_date": "2026-07-08T14:16:44.108829",
  "status": "active"
}
PUTapi.aquabotany.com/subscription

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();
200 Response
{
  "message": "Subscription updated"
}
DELETEapi.aquabotany.com/subscription

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();
200 Response
{
  "message": "Subscription deleted"
}
GETapi.aquabotany.com/subscription-plans

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();
200 Response
{
  "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

GETapi.aquabotany.com/workorder

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 }
});
200 Response
[
  {
    "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"
  }
]
POSTapi.aquabotany.com/workorder

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"})
});
200 Response
{
  "status": "created",
  "workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}
GETapi.aquabotany.com/workorder/{workorderId}

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.

ParameterInDescription
workorderIdpathrequired 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 }
});
200 Response
{
  "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"
}
PUTapi.aquabotany.com/workorder/{workorderId}

Update a work order. Body: any of title, department, assigned_to, priority, status, notes. 400 if no fields supplied. Returns {"status": "updated", ...}.

ParameterInDescription
workorderIdpathrequired 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"})
});
200 Response
{
  "status": "updated",
  "workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}
PATCHapi.aquabotany.com/workorder/{workorderId}

Same as PUT. Body: any of title, department, assigned_to, priority, status, notes. 400 if no fields supplied. Returns {"status": "updated", ...}.

ParameterInDescription
workorderIdpathrequired 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"})
});
200 Response
{
  "status": "updated",
  "workorder_id": "5d1c8a4e-2f3b-4c6d-9e0f-1a2b3c4d5e6f"
}
DELETEapi.aquabotany.com/workorder/{workorderId}

Delete a work order. Returns {"status": "deleted", ...}. Scoped to the authenticated user; another user's IDs return 404.

ParameterInDescription
workorderIdpathrequired 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 }
});
200 Response
{
  "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.

GETapi.pocketquant.io/dashboard

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();
200 Response
{
  "message": "Dashboard"
}
GETapi.pocketquant.io/health

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();
200 Response
{
  "status": "ok",
  "service": "Pocket Quant API",
  "message": "API is healthy and responding.",
  "timestamp": "2026-07-08T14:32:07.412896+00:00"
}
GETapi.pocketquant.io/mobile

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();
200 Response
{
  "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."
    }
  }
}
POSTapi.pocketquant.io/search

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();
200 Response
[
  {
    "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"
  }
]
GETapi.pocketquant.io/stats/{ticker}

Statistics for a ticker.

ParameterInDescription
tickerpathrequired 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();
200 Response
{
  "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
  }
}
GETapi.pocketquant.io/stocks/{ticker}

Quote and data for a ticker.

ParameterInDescription
tickerpathrequired 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();
200 Response
{
  "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": []
}
POSTapi.pocketquant.io/subscribe

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();
200 Response
{
  "message": "Subscribed successfully"
}
DELETEapi.pocketquant.io/unsubscribe

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();
200 Response
{
  "message": "Unsubscribed successfully"
}

Other

Base URL https://bottlebluellc.com

POSTbottlebluellc.com/contact

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();
200 Response
{
  "message": "Message received!"
}
GETbottlebluellc.com/health

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();
200 Response
{
  "status": "ok",
  "message": "Endpoint is live"
}
GETcloud.bottlebluellc.com/reading

Latest sensor readings for the caller (newest first).

ParameterInDescription
deviceIdqueryoptional Restrict to one device serial.
limitqueryoptional Page size (default 10, max 100).
startKeyqueryoptional 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();
200 Response
{
  "Items": [
    {
      "deviceId": "SN-00142",
      "ts": 1783503000,
      "sensorType": "moisture",
      "value": 42.0
    }
  ],
  "count": 1,
  "deviceId": null,
  "nextKey": null
}
POSTbottlebluellc.com/subscribe

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();
200 Response
{
  "message": "Successfully subscribed!"
}