Introduction

The Garden.gg API provides programmatic access to your gardens, plants, events, achievements, and more. Build integrations, automate your garden logging, or create custom dashboards with our RESTful JSON API.

Base URL

All API requests are made to:

https://garden.gg/api/v1

All requests and responses use application/json content type unless otherwise noted (e.g., multipart file uploads).

Authentication

Every request to a protected endpoint must include an Authorization header with a Bearer token. You can authenticate using either a JWT access token (obtained via the login flow) or an API key.

Authorization: Bearer <token>

API keys are prefixed with gg_live_ and can be generated in your account settings. They do not expire but can be revoked at any time. See the Authentication page for full details.

Hardware integrations (Ecowitt gateways, ESP32 builds) use a third shape: a device token in the URL path. See IoT & Devices.

Quick Start Examples

cURL

curl -X GET "https://garden.gg/api/v1/gardens" \
  -H "Authorization: Bearer gg_live_your_api_key_here" \
  -H "Content-Type: application/json"

Python

import requests

API_KEY = "gg_live_your_api_key_here"
BASE_URL = "https://garden.gg/api/v1"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

response = requests.get(f"{BASE_URL}/gardens", headers=headers)
print(response.json())

Node.js

const API_KEY = "gg_live_your_api_key_here";
const BASE_URL = "https://garden.gg/api/v1";

const response = await fetch(`${BASE_URL}/gardens`, {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
});

const data = await response.json();
console.log(data);

Rate Limits

Rate limits depend on your plan tier:

PlanRequests per Minute
Bloom100
Harvest1,000

Rate limit information is included in every response via headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per minute
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

When you exceed your rate limit, the API returns a 429 Too Many Requests response with the RATE_LIMIT_EXCEEDED error code. Wait until the X-RateLimit-Reset timestamp before retrying.

{
  "error": "Rate limit exceeded. Try again in 23 seconds.",
  "code": "RATE_LIMIT_EXCEEDED"
}

Pagination

Endpoints that return lists support pagination via query parameters:

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed)
per_pageinteger20Items per page (max 100)

Paginated responses include metadata alongside the results:

{
  "data": [ ... ],
  "total": 87,
  "page": 2,
  "per_page": 20
}

Pagination Example

curl -X GET "https://garden.gg/api/v1/gardens?page=2&per_page=10" \
  -H "Authorization: Bearer gg_live_your_api_key_here"

Error Handling

The API uses standard HTTP status codes and returns errors in a consistent JSON format:

{
  "error": "A human-readable error message.",
  "code": "ERROR_CODE"
}

HTTP Status Codes

Status CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
204No ContentRequest succeeded with no response body
400Bad RequestInvalid request parameters or body
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but lacking permission
404Not FoundResource does not exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error

Common Error Codes

CodeHTTP StatusDescription
UNAUTHORIZED401Missing or invalid token
FORBIDDEN403You do not have access to this resource
NOT_FOUND404The requested resource was not found
VALIDATION_ERROR400Request body failed validation
RATE_LIMIT_EXCEEDED429Too many requests in the current window

Error Handling Example

import requests

response = requests.get(
    "https://garden.gg/api/v1/gardens/nonexistent-id",
    headers={"Authorization": "Bearer gg_live_your_api_key_here"},
)

if response.status_code == 200:
    garden = response.json()
elif response.status_code == 404:
    error = response.json()
    print(f"Not found: {error['error']}")
elif response.status_code == 401:
    print("Check your API key or token")
else:
    error = response.json()
    print(f"Error {response.status_code}: {error['error']} ({error['code']})")

Date and Time Format

All dates and timestamps in the API use RFC 3339 format:

2026-03-15T14:30:00Z
2026-06-01T09:00:00-05:00

Timestamps are returned in UTC unless otherwise noted. You may submit timestamps with timezone offsets and they will be stored correctly.

Versioning

The API is versioned via the URL path (/api/v1/). Breaking changes will be introduced under a new version (e.g., /api/v2/). Non-breaking additions (new fields, new endpoints) may be added to the current version without notice.

Next Steps