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:
| Plan | Requests per Minute |
|---|---|
| Bloom | 100 |
| Harvest | 1,000 |
Rate limit information is included in every response via headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per minute |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix 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:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-indexed) |
per_page | integer | 20 | Items 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 Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded with no response body |
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but lacking permission |
| 404 | Not Found | Resource does not exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid token |
FORBIDDEN | 403 | You do not have access to this resource |
NOT_FOUND | 404 | The requested resource was not found |
VALIDATION_ERROR | 400 | Request body failed validation |
RATE_LIMIT_EXCEEDED | 429 | Too 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
- Authentication — Set up JWT login or API keys
- Gardens — Create and manage your gardens
- Achievements — Explore gamification endpoints