Skip to main content

Overview

Base URL: https://api.petstoreapi.com/v1 Protocol: HTTPS (REST over HTTP/1.1 or HTTP/2) Content-Type: application/json The REST API follows all REST architectural constraints:
  • Client-Server: Clear separation of concerns
  • Stateless: Each request contains all necessary information
  • Cacheable: Responses include cache headers
  • Uniform Interface: Consistent resource-oriented design
  • Layered System: No client visibility into server architecture

RESTful Design Principles

1. Resource-Oriented URLs

URLs represent resources (nouns), not actions (verbs):
✅ CORRECT:
GET    /pets           # List pets
POST   /pets           # Create a pet
GET    /pets/{id}      # Get a specific pet
PUT    /pets/{id}      # Update a pet
DELETE /pets/{id}      # Delete a pet

❌ WRONG:
/getPets
/createPet
/updatePet
/deletePet
/pet/findByStatus     # Use query parameters instead

2. Proper HTTP Methods

MethodSafeIdempotentPurpose
GETRetrieve resources
POSTCreate resources or submit forms
PUTFull update (replace)
PATCHPartial update
DELETEDelete resources
QUERYComplex queries with body (OpenAPI 3.2)
Example:
# GET - Safe and idempotent
curl https://api.petstoreapi.com/v1/pets

# POST - Not idempotent (creates new resource)
curl -X POST https://api.petstoreapi.com/v1/pets \
  -H "Content-Type: application/json" \
  -d '{"name": "Buddy", "species": "DOG"}'

# DELETE - Idempotent (deleting twice = deleting once)
curl -X DELETE https://api.petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24

3. Correct HTTP Status Codes

# Success Codes
200 OK               # GET, PUT, PATCH success
201 Created          # POST success (includes Location header)
204 No Content       # DELETE success (no response body)

# Client Error Codes
400 Bad Request      # Malformed request
401 Unauthorized     # Missing or invalid authentication
403 Forbidden        # Authenticated but lacks permission
404 Not Found        # Resource doesn't exist
409 Conflict         # Resource conflict (duplicate)
422 Unprocessable    # Validation failed

# Server Error Codes
500 Internal Error   # Server-side failure
503 Service Unavailable # Temporary disruption
Example:
curl -i -X POST https://api.petstoreapi.com/v1/pets \
  -H "Content-Type: application/json" \
  -d '{"name": "Buddy", "species": "DOG"}'

# Response:
HTTP/1.1 201 Created
Location: https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw
Content-Type: application/json

{
  "id": "pet_fYrZzCW9E1WIOyGw",
  "name": "Buddy",
  "species": "DOG",
  "status": "AVAILABLE"
}

4. Collection Wrappers

Never return bare arrays. Always wrap collections with metadata:
{
  "data": [
    { "id": "019b4132-70aa-764f-b315-e2803d882a24", "name": "Buddy", "species": "DOG" },
    { "id": "019b4127-54d5-76d9-b626-0d4c7bfce5b6", "name": "Whiskers", "species": "CAT" }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalItems": 145,
    "totalPages": 8
  },
  "links": {
    "self": "https://api.petstoreapi.com/v1/pets?page=1",
    "next": "https://api.petstoreapi.com/v1/pets?page=2",
    "prev": null,
    "first": "https://api.petstoreapi.com/v1/pets?page=1",
    "last": "https://api.petstoreapi.com/v1/pets?page=8"
  }
}

5. Plural Resource Names

Always use plural nouns for collections:
✅ CORRECT:
/pets           # Collection of pets
/pets/{id}      # Single pet from collection
/users          # Collection of users
/orders         # Collection of orders

❌ WRONG:
/pet            # Singular
/user           # Singular
/order          # Singular

Authentication

Bearer Token (JWT)

Include the token in the Authorization header:
curl https://api.petstoreapi.com/v1/pets \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Multi-Tenancy

Isolate data using the X-Tenant-ID header:
curl https://api.petstoreapi.com/v1/pets \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer YOUR_TOKEN"

Endpoints

Pets

# List pets with filtering
curl "https://api.petstoreapi.com/v1/pets?species=DOG&status=AVAILABLE&page=1&limit=20"

# Get a specific pet
curl https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw

# Create a new pet
curl -X POST https://api.petstoreapi.com/v1/pets \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "name": "Buddy",
    "species": "DOG",
    "breed": "Golden Retriever",
    "ageMonths": 24,
    "status": "AVAILABLE",
    "adoptionFee": 150.00,
    "currency": "USD"
  }'

# Update a pet (full replacement)
curl -X PUT https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "name": "Buddy",
    "species": "DOG",
    "breed": "Golden Retriever",
    "ageMonths": 25,
    "status": "PENDING"
  }'

# Partial update
curl -X PATCH https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"status": "AVAILABLE"}'

# Delete a pet
curl -X DELETE https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw \
  -H "Authorization: Bearer YOUR_TOKEN"

Advanced Search (QUERY Method)

OpenAPI 3.2 introduces the QUERY method for complex searches:
curl -X QUERY https://api.petstoreapi.com/v1/pets/search \
  -H "Content-Type: application/json" \
  -d '{
    "criteria": {
      "species": ["DOG", "CAT"],
      "ageRange": {"min": 12, "max": 36},
      "compatibility": {"goodWithKids": true}
    },
    "sort": {"field": "ageMonths", "order": "ASC"},
    "pagination": {"page": 1, "limit": 20}
  }'

Orders

# Create an order
curl -X POST https://api.petstoreapi.com/v1/orders \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "items": [
      {
        "productId": "019b4132-70aa-764f-b315-e2803d882a24",
        "quantity": 2,
        "price": 29.99
      }
    ],
    "shippingAddress": {
      "street": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94102",
      "country": "US"
    }
  }'

Error Handling

All errors follow RFC 9457 Problem Details format:
{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request body contains validation errors",
  "instance": "/v1/pets",
  "errors": [
    {
      "field": "ageMonths",
      "message": "Must be a positive number",
      "code": "INVALID_FORMAT"
    },
    {
      "field": "species",
      "message": "Must be one of: DOG, CAT, RABBIT, BIRD, REPTILE, OTHER",
      "code": "INVALID_ENUM_VALUE"
    }
  ]
}

Rate Limiting

The API uses IETF standard rate limiting headers:
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 1640000000
When exceeded:
{
  "type": "https://petstoreapi.com/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit of 100 requests per minute",
  "instance": "/v1/pets"
}

Caching

Resources include cache headers:
Cache-Control: public, max-age=300
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Tue, 15 Nov 2025 12:45:26 GMT
Conditional requests:
curl https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw \
  -H "If-None-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\""

# Response: 304 Not Modified (if unchanged)

Resources include HATEOAS-style links:
{
  "id": "pet_fYrZzCW9E1WIOyGw",
  "name": "Whiskers",
  "species": "CAT",
  "status": "AVAILABLE",
  "links": {
    "self": "https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw",
    "adopt": "https://api.petstoreapi.com/v1/adoptions",
    "images": "https://api.petstoreapi.com/v1/pets/pet_fYrZzCW9E1WIOyGw/images"
  }
}

Code Examples

TypeScript

import { PetStoreAPI } from '@petstoreapi/sdk';

const client = new PetStoreAPI({
  apiKey: process.env.PETSTORE_API_KEY
});

// List pets
const pets = await client.pets.list({ species: 'DOG', status: 'AVAILABLE' });

// Get a pet
const pet = await client.pets.get('pet_fYrZzCW9E1WIOyGw');

// Create a pet
const newPet = await client.pets.create({
  name: 'Buddy',
  species: 'DOG',
  breed: 'Golden Retriever',
  ageMonths: 24,
  status: 'AVAILABLE'
});

Python

from petstore import PetStoreAPI

client = PetStoreAPI(api_key=os.environ['PETSTORE_API_KEY'])

# List pets
pets = client.pets.list(species='DOG', status='AVAILABLE')

# Get a pet
pet = client.pets.get('pet_fYrZzCW9E1WIOyGw')

# Create a pet
new_pet = client.pets.create(
    name='Buddy',
    species='DOG',
    breed='Golden Retriever',
    age_months=24,
    status='AVAILABLE'
)

Best Practices

1. Follow HTTP Semantics

  • Use GET for retrieval (safe, idempotent)
  • Use POST for creation (not idempotent)
  • Use PUT for full replacement (idempotent)
  • Use PATCH for partial updates (idempotent)
  • Use DELETE for removal (idempotent)

2. Handle Status Codes Correctly

const response = await fetch('https://api.petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24');

switch (response.status) {
  case 200: return await response.json();           // OK
  case 404: throw new Error('Not found');          // Not found
  case 422: throw new Error(await response.json()); // Validation error
  default: throw new Error('Unexpected status');
}

3. Respect Rate Limits

const checkRateLimit = (response) => {
  const remaining = response.headers.get('RateLimit-Remaining');
  const reset = response.headers.get('RateLimit-Reset');

  if (remaining === '0') {
    const waitUntil = new Date(parseInt(reset) * 1000);
    console.log(`Rate limited. Wait until ${waitUntil}`);
  }
};

4. Use Conditional Requests

// Save ETag
let etag = null;

// First request
const response1 = await fetch(url);
etag = response1.headers.get('ETag');

// Subsequent request with conditional check
const response2 = await fetch(url, {
  headers: { 'If-None-Match': etag }
});

if (response2.status === 304) {
  console.log('Content unchanged');
}

Interactive Documentation