Overview

The Users API covers public profiles, account management, password changes, and API key management. Public profiles are viewable by anyone; account endpoints require authentication and operate on the authenticated user’s own data.

Endpoints

MethodPathDescriptionAuth Required
GET/users/:usernameGet a public profileNo
GET/accountGet your account detailsYes
PUT/accountUpdate your accountYes
PUT/account/passwordChange your passwordYes
GET/account/api-keysList your API keysYes
POST/account/api-keysCreate an API keyYes
DELETE/account/api-keys/:idRevoke an API keyYes

Public Profile

Retrieve a user’s public profile. This endpoint does not require authentication.

GET /users/:username

Path Parameters

ParameterTypeDescription
usernamestringThe user’s username

Response 200 OK

{
  "username": "green_thumb",
  "display_name": "Green Thumb",
  "bio": "Urban gardener in Portland, OR. Zone 8b. Growing tomatoes, peppers, and herbs.",
  "avatar_url": "https://cdn.garden.gg/avatars/green_thumb.jpg",
  "zone": "8b",
  "level": 12,
  "xp": 2450,
  "badge_count": 8,
  "badges": [
    {
      "id": "bdg_seedling",
      "name": "Seedling",
      "icon_url": "https://cdn.garden.gg/badges/seedling.png",
      "tier": "bronze"
    },
    {
      "id": "bdg_streak_7",
      "name": "Week Warrior",
      "icon_url": "https://cdn.garden.gg/badges/streak_7.png",
      "tier": "bronze"
    }
  ],
  "public_gardens": [
    {
      "id": "gdn_a1b2c3d4",
      "name": "Backyard Paradise",
      "location": "Portland, OR",
      "zone": "8b",
      "plot_count": 4
    }
  ],
  "joined_at": "2026-01-10T00:00:00Z"
}

Examples

cURL

curl -X GET "https://garden.gg/api/v1/users/green_thumb"

Python

import requests

response = requests.get("https://garden.gg/api/v1/users/green_thumb")
profile = response.json()

print(f"{profile['display_name']} — Level {profile['level']}")
print(f"Bio: {profile['bio']}")
print(f"Badges: {profile['badge_count']}")
for garden in profile["public_gardens"]:
    print(f"  Garden: {garden['name']} ({garden['plot_count']} plots)")

Node.js

const response = await fetch("https://garden.gg/api/v1/users/green_thumb");
const profile = await response.json();

console.log(`${profile.display_name} — Level ${profile.level}`);
console.log(`Bio: ${profile.bio}`);
profile.public_gardens.forEach((g) =>
  console.log(`  Garden: ${g.name} (${g.plot_count} plots)`)
);

Get Account

Retrieve your own account details. Includes private information not shown on the public profile.

GET /account

Response 200 OK

{
  "id": "usr_a1b2c3d4",
  "email": "[email protected]",
  "username": "green_thumb",
  "display_name": "Green Thumb",
  "bio": "Urban gardener in Portland, OR. Zone 8b.",
  "avatar_url": "https://cdn.garden.gg/avatars/green_thumb.jpg",
  "zone": "8b",
  "level": 12,
  "xp": 2450,
  "plan": "bloom",
  "email_verified": true,
  "created_at": "2026-01-10T00:00:00Z",
  "updated_at": "2026-03-15T09:00:00Z"
}

Examples

cURL

curl -X GET "https://garden.gg/api/v1/account" \
  -H "Authorization: Bearer gg_live_your_api_key_here"

Python

import requests

headers = {"Authorization": "Bearer gg_live_your_api_key_here"}
response = requests.get("https://garden.gg/api/v1/account", headers=headers)
account = response.json()

print(f"Email: {account['email']}")
print(f"Plan: {account['plan']}")
print(f"Level: {account['level']} ({account['xp']} XP)")

Node.js

const response = await fetch("https://garden.gg/api/v1/account", {
  headers: { Authorization: "Bearer gg_live_your_api_key_here" },
});

const account = await response.json();
console.log(`Email: ${account.email}`);
console.log(`Plan: ${account.plan}`);
console.log(`Level: ${account.level} (${account.xp} XP)`);

Update Account

Update your account details. Only include the fields you want to change.

PUT /account

Request Body

FieldTypeRequiredDescription
display_namestringNoDisplay name (1-50 characters)
biostringNoProfile bio (max 500 characters)
avatarfileNoAvatar image (multipart upload)
zonestringNoUSDA hardiness zone (e.g., “8b”)

Request

{
  "display_name": "Green Thumb Pro",
  "bio": "Urban gardener and composting enthusiast in Portland, OR.",
  "zone": "8b"
}

Response 200 OK

Returns the full updated account object.

Examples

cURL

curl -X PUT "https://garden.gg/api/v1/account" \
  -H "Authorization: Bearer gg_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Green Thumb Pro",
    "bio": "Urban gardener and composting enthusiast in Portland, OR."
  }'

cURL (with avatar upload)

curl -X PUT "https://garden.gg/api/v1/account" \
  -H "Authorization: Bearer gg_live_your_api_key_here" \
  -F "display_name=Green Thumb Pro" \
  -F "[email protected]"

Python

import requests

response = requests.put(
    "https://garden.gg/api/v1/account",
    headers={
        "Authorization": "Bearer gg_live_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={
        "display_name": "Green Thumb Pro",
        "bio": "Urban gardener and composting enthusiast.",
        "zone": "8b",
    },
)

account = response.json()
print(f"Updated: {account['display_name']}")

Node.js

const response = await fetch("https://garden.gg/api/v1/account", {
  method: "PUT",
  headers: {
    Authorization: "Bearer gg_live_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    display_name: "Green Thumb Pro",
    bio: "Urban gardener and composting enthusiast.",
    zone: "8b",
  }),
});

const account = await response.json();
console.log(`Updated: ${account.display_name}`);

Change Password

Change your account password. Requires your current password for verification.

PUT /account/password

Request Body

FieldTypeRequiredDescription
current_passwordstringYesYour current password
new_passwordstringYesNew password (min 8 characters)

Request

{
  "current_password": "old_s3cur3p4ss",
  "new_password": "new_s3cur3p4ss"
}

Response 200 OK

{
  "message": "Password updated successfully."
}

Examples

cURL

curl -X PUT "https://garden.gg/api/v1/account/password" \
  -H "Authorization: Bearer gg_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "old_s3cur3p4ss",
    "new_password": "new_s3cur3p4ss"
  }'

Python

import requests

response = requests.put(
    "https://garden.gg/api/v1/account/password",
    headers={
        "Authorization": "Bearer gg_live_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={
        "current_password": "old_s3cur3p4ss",
        "new_password": "new_s3cur3p4ss",
    },
)

print(response.json()["message"])

Node.js

const response = await fetch("https://garden.gg/api/v1/account/password", {
  method: "PUT",
  headers: {
    Authorization: "Bearer gg_live_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    current_password: "old_s3cur3p4ss",
    new_password: "new_s3cur3p4ss",
  }),
});

const result = await response.json();
console.log(result.message);

List API Keys

Retrieve all API keys associated with your account. The full key value is not returned; only the last 4 characters are shown.

GET /account/api-keys

Response 200 OK

{
  "data": [
    {
      "id": "key_x9y8z7",
      "name": "CI Pipeline",
      "key_suffix": "o345",
      "created_at": "2026-02-01T10:00:00Z",
      "last_used_at": "2026-03-15T08:30:00Z"
    },
    {
      "id": "key_w6v5u4",
      "name": "Home Assistant",
      "key_suffix": "p678",
      "created_at": "2026-03-01T14:00:00Z",
      "last_used_at": null
    }
  ]
}

Create API Key

Generate a new API key. The full key is only returned once.

POST /account/api-keys

Request Body

FieldTypeRequiredDescription
namestringYesA label for this key (1-50 chars)

Request

{
  "name": "Greenhouse Monitor"
}

Response 201 Created

{
  "id": "key_t3s2r1",
  "name": "Greenhouse Monitor",
  "key": "gg_live_abc123def456ghi789jkl012mno345",
  "created_at": "2026-03-15T10:00:00Z"
}

Store the key value securely. It will not be shown again.

Examples

cURL

curl -X POST "https://garden.gg/api/v1/account/api-keys" \
  -H "Authorization: Bearer gg_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "Greenhouse Monitor"}'

Python

import requests

response = requests.post(
    "https://garden.gg/api/v1/account/api-keys",
    headers={
        "Authorization": "Bearer gg_live_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={"name": "Greenhouse Monitor"},
)

key_data = response.json()
print(f"API Key created: {key_data['key']}")
print("Store this key securely — it will not be shown again.")

Node.js

const response = await fetch("https://garden.gg/api/v1/account/api-keys", {
  method: "POST",
  headers: {
    Authorization: "Bearer gg_live_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Greenhouse Monitor" }),
});

const keyData = await response.json();
console.log(`API Key created: ${keyData.key}`);
console.log("Store this key securely — it will not be shown again.");

Revoke API Key

Permanently revoke an API key. Any requests using this key will immediately start returning 401 Unauthorized.

DELETE /account/api-keys/:id

Path Parameters

ParameterTypeDescription
idstringThe API key’s ID

Response 204 No Content

No response body.

Examples

cURL

curl -X DELETE "https://garden.gg/api/v1/account/api-keys/key_w6v5u4" \
  -H "Authorization: Bearer gg_live_your_api_key_here"

Python

import requests

response = requests.delete(
    "https://garden.gg/api/v1/account/api-keys/key_w6v5u4",
    headers={"Authorization": "Bearer gg_live_your_api_key_here"},
)

if response.status_code == 204:
    print("API key revoked successfully.")

Node.js

const response = await fetch(
  "https://garden.gg/api/v1/account/api-keys/key_w6v5u4",
  {
    method: "DELETE",
    headers: { Authorization: "Bearer gg_live_your_api_key_here" },
  }
);

if (response.status === 204) {
  console.log("API key revoked successfully.");
}

Error Responses

ScenarioHTTP StatusError Code
User not found404NOT_FOUND
API key not found404NOT_FOUND
Current password incorrect400VALIDATION_ERROR
New password too short400VALIDATION_ERROR
Missing required name field400VALIDATION_ERROR
Avatar file too large400VALIDATION_ERROR
Not authenticated401UNAUTHORIZED