Use the PawPlacer public API to fetch lightweight pet data for embeds, websites, and partner integrations. All endpoints require an API key sent in the x-api-key header and return only pets marked as public.
Endpoint Summary
| Endpoint | Purpose | Rate Limit |
|---|---|---|
GET /api/pets | Paginated list of public pets | 100 requests/hour |
GET /api/pets?… | Filtered slice using query parameters | 100 requests/hour |
GET /api/pets/{petId} | Single public pet | 400 requests/hour |
GET /api/pets/custom-fields | Custom field metadata for pet forms | 15 requests/hour |
Cache responses for at least a few minutes to stay well below the hourly limits. The payloads are designed to be CDN-cacheable and stable.
OpenAPI 3.1 contract: https://pawplacer.com/openapi/public-api-v1.yaml
Standard Response Headers
All public pet endpoints now include diagnostic and rate-limit headers:
X-Request-Id— unique request identifier for support/debuggingX-Api-Version— current public API contract versionX-Generated-At— response generation timestamp (ISO-8601)X-RateLimit-Limit— endpoint hourly limitX-RateLimit-Remaining— remaining calls in current window (-1when unavailable)X-RateLimit-Reset— Unix timestamp for next limit reset windowRetry-After— present on429responses
GET /api/pets – List Public Pets
Returns a paginated list of public pets with a full pet profile payload (including custom fields and metadata).
Request
GET https://pawplacer.com/api/pets
x-api-key: YOUR_API_KEY
Optional query parameters
limit— 1–100 (default 20)offset— results to skip (default 0)species—dog,cat, orrabbitstatus— matches your custom status labels (case-insensitive)search— fuzzy match on name, description, and breed valuesupdated_since— ISO-8601 timestamp; returns only pets withupdated_at >= updated_since
JavaScript example
const response = await fetch('https://pawplacer.com/api/pets?status=available&species=dog&limit=12', {
headers: { 'x-api-key': process.env.PAWPLACER_API_KEY }
});
const { pets, total, hasMore } = await response.json();
Response
{
"pets": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Max",
"species": "dog",
"status": "Available",
"health": "good",
"show_public": true,
"breed": ["Labrador Retriever"],
"color": ["Black"],
"age_category": "young",
"age_years": null,
"age_months": null,
"age_birthday": null,
"sex": "male",
"size": "medium",
"description": "Friendly and energetic dog",
"spayed": true,
"adoption_fee": "250",
"microchip_id": null,
"good_with": ["families", "kids"],
"bad_with": [],
"temperaments": ["playful", "social"],
"image_urls": [
"https://pawplacer.com/pets/max.jpg",
"https://pawplacer.com/pets/max-2.jpg"
],
"image_url": "https://pawplacer.com/pets/max.jpg",
"coat_length": "short",
"custom_id": "EXT-1001",
"custom_field_data": {
"favorite_toy": "Tennis ball"
},
"intake_date": null,
"outcome_date": null,
"primary_veterinarian": "Downtown Vet Clinic",
"special_needs": [],
"tags": ["Senior", "Special Care"],
"status_change_notes": null,
"weight": null,
"adopted_on": null,
"adopted_by": "Jane Foster",
"created_at": "2026-02-17T12:00:00.000000+00:00",
"updated_at": "2026-02-17T12:00:00.000000+00:00"
}
],
"total": 42,
"limit": 12,
"offset": 0,
"hasMore": true
}
Fields returned
pets— array of public pets. Each object includes a full profile payload:- Core:
id,name,species,status,health,show_public - Demographics:
breed,color,age_category,age_years,age_months,age_birthday,sex,size,weight,spayed - Behavior/compatibility:
good_with,bad_with,temperaments,special_needs - Tags:
tags(pet tag labels) - Identifiers:
custom_id,microchip_id - Notes + dates:
description,intake_date,outcome_date,status_change_notes - Relationships (resolved names):
primary_veterinarian,adopted_by - Media:
image_urls,image_url,coat_length - Custom data:
custom_field_data - Metadata:
created_at,updated_at,adopted_on
- Core:
total,limit,offset,hasMore— pagination metadata
GET /api/pets/{petId} – Single Pet
Fetch a single public pet by ID. This endpoint returns the same field set as the list endpoint.
Request
GET https://pawplacer.com/api/pets/{petId}
x-api-key: YOUR_API_KEY
JavaScript example
const response = await fetch(`https://pawplacer.com/api/pets/${petId}`, {
headers: { 'x-api-key': process.env.PAWPLACER_API_KEY }
});
const pet = await response.json();
If the pet is not public, deleted, or does not exist, the API responds with 404 and { "error": "Pet not found" }.
GET /api/pets/custom-fields – Custom Field Metadata
Retrieve configured custom form fields so you can send valid keys when creating pets.
Request
GET https://pawplacer.com/api/pets/custom-fields
x-api-key: YOUR_API_KEY
Response
{
"custom_fields": [
{
"field_key": "favorite_toy",
"label": "Favorite Toy",
"field_type": "text",
"required": false,
"help_text": "Include any comfort objects",
"options": null,
"section": "Behavior"
}
]
}
Use the field_key (not the label) when populating custom_field_data in POST /api/pets requests.
Rate Limit Errors
Rate limits are enforced per organization on a rolling hourly window. If the limit is exceeded, the response is 429 with a descriptive error payload, for example:
{
"error": "Rate limit exceeded. Maximum 100 requests per hour. Cache responses to avoid throttling.",
"code": "rate_limited",
"request_id": "3dfabc8f-3f6f-4f4e-bffe-5d0dc5292e6f"
}
Reduce the frequency of calls or increase your cache window before retrying.
Error Reference
| Status | Reason | Example Payload |
|---|---|---|
| 401 | Missing or invalid API key | { "error": "API key required", "code": "api_key_required", "request_id": "..." } |
| 404 | Pet not found | { "error": "Pet not found", "code": "pet_not_found", "request_id": "..." } |
| 429 | Rate limit exceeded | see example above |
| 500 | Unexpected server issue | { "error": "Something went wrong", "code": "internal_error", "request_id": "..." } |
Always handle non-200 status codes and back off when you see 429 responses.