Skip to content

Latest commit

 

History

History
680 lines (573 loc) · 17.3 KB

File metadata and controls

680 lines (573 loc) · 17.3 KB

Resy API Documentation

Complete documentation for the Resy booking API, reverse-engineered from production traffic.

Table of Contents


Authentication

Resy uses two authentication mechanisms:

1. API Key (Public)

Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"
  • Required for all API requests
  • Public key embedded in Resy's website/app
  • Rate-limited per key

2. Auth Token (User-specific)

X-Resy-Auth-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
X-Resy-Universal-Auth: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
  • JWT token containing user identity
  • Required for authenticated endpoints (availability, booking, user data)
  • Expires after ~45 days
  • Both headers should have the same value

How to Get Auth Token

Option 1: From Browser (Recommended)

  1. Open Resy website and log in
  2. Open DevTools → Network tab
  3. Find any api.resy.com request
  4. Copy the X-Resy-Auth-Token header value

Option 2: Programmatic Login

POST https://api.resy.com/3/auth/password
Content-Type: application/x-www-form-urlencoded

email=your@email.com&password=yourpassword

Response:

{
  "auth_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...",
  "payment_method_id": "12345678"
}

Complete Booking Flow

┌─────────────────────────────────────────────────────────────┐
│                      Resy Booking Flow                       │
└─────────────────────────────────────────────────────────────┘

1. Search Restaurants
   └─> POST /3/venuesearch/search
       Input: Search query (e.g., "carbone")
       Output: List of venues with IDs
       ↓
       
2. Check Availability
   └─> GET /4/find
       Input: venue_id, date, party_size
       Output: Available time slots with config tokens (rgs://...)
       ↓
       
3. Get Booking Details ⭐ CRITICAL STEP
   └─> POST /3/details
       Input: config_id (from step 2), date, party_size
       Output: Real encrypted book token + payment methods
       ↓
       
4. Book Reservation
   └─> POST /3/book
       Input: book_token (from step 3), payment_method_id
       Output: Confirmation number + resy_token

Why Step 3 is Critical:

  • Step 2 returns a rgs://resy/... token (config token)
  • This is NOT the booking token
  • Step 3 converts it into the encrypted booking token
  • Without this step, booking will fail with "invalid book token"

API Endpoints

1. Search Restaurants

Find restaurants by name, cuisine, or keyword.

Request

POST https://api.resy.com/3/venuesearch/search
Content-Type: application/json
Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"

{
  "query": "carbone"
}

Response

{
  "search": {
    "hits": [
      {
        "id": {
          "resy": 6194
        },
        "name": "Carbone",
        "type": "Restaurant",
        "url_slug": "carbone",
        "locality": "New York",
        "region": "NY",
        "neighborhood": "Greenwich Village",
        "cuisine": ["Italian"],
        "price_range_id": 4,
        "min_party_size": 2,
        "max_party_size": 6,
        "rating": {
          "average": 4.8,
          "count": 15234
        },
        "location": {
          "address_1": "181 Thompson St",
          "city": "New York",
          "state": "NY",
          "zip": "10012",
          "latitude": 40.7285,
          "longitude": -74.0019
        }
      }
    ]
  }
}

Notes

  • No authentication required (public search)
  • Returns up to 50 results
  • Price range: 1 ($), 2 ($$), 3 ($$$), 4 ($$$$)

2. Check Availability

Get available reservation slots for a specific venue, date, and party size.

Request

GET https://api.resy.com/4/find?venue_id=6194&day=2025-10-16&party_size=2&lat=0&long=0
Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"
X-Resy-Auth-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
X-Resy-Universal-Auth: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Origin: https://resy.com
Referer: https://resy.com/

Query Parameters

Parameter Type Required Description
venue_id int Yes Restaurant venue ID from search
day string Yes Date in YYYY-MM-DD format
party_size int Yes Number of guests (1-20)
lat float Yes Latitude (use 0 if unknown)
long float Yes Longitude (use 0 if unknown)

Response

{
  "results": {
    "venues": [
      {
        "venue": {
          "id": { "resy": 6194 },
          "name": "Carbone"
        },
        "slots": [
          {
            "date": {
              "start": "2025-10-16 18:00:00",
              "end": "2025-10-16 20:00:00"
            },
            "config": {
              "id": 1521664,
              "type": "Dining Room",
              "token": "rgs://resy/6194/1521664/2/2025-10-16/2025-10-16/18:00:00/2/Dining Room"
            }
          },
          {
            "date": {
              "start": "2025-10-16 18:15:00",
              "end": "2025-10-16 20:15:00"
            },
            "config": {
              "id": 1521665,
              "type": "Bar Counter",
              "token": "rgs://resy/6194/1521665/2/2025-10-16/2025-10-16/18:15:00/2/Bar Counter"
            }
          }
        ]
      }
    ]
  }
}

Important Notes

  • ⚠️ The config.token is NOT the booking token
  • This token must be passed to /3/details to get the real booking token
  • Slots may disappear quickly (high-demand restaurants)
  • config.type indicates seating type (Dining Room, Bar, Patio, etc.)

3. Get Booking Details

Critical step: Converts config token to real booking token.

Request

POST https://api.resy.com/3/details
Content-Type: application/json
Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"
X-Resy-Auth-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
X-Resy-Universal-Auth: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Origin: https://resy.com
Referer: https://resy.com/

{
  "config_id": "rgs://resy/6194/1521665/2/2025-10-16/2025-10-16/18:15:00/2/Bar Counter",
  "day": "2025-10-16",
  "party_size": 2
}

Request Body

Field Type Required Description
config_id string Yes Config token from /4/find
day string Yes Date in YYYY-MM-DD format
party_size int Yes Number of guests
commit_id int No For modifying existing reservations

Response

{
  "book_token": {
    "value": "7TWgoK_Vi5aSUfHvc6pN|jXBy9PG_lcc13e7pgMAYeLAPKKyqs_G2PrGSi4X32SK...",
    "date_expires": "2025-10-09T23:01:37Z"
  },
  "user": {
    "payment_methods": [
      {
        "id": 31876445,
        "is_default": true,
        "provider": "stripe",
        "display": "1004"
      }
    ]
  },
  "cancellation": {
    "display_text": "Cancel by 6:00 PM on Oct 15 for a full refund",
    "fee": {
      "amount": 25.00
    }
  },
  "change": {
    "display_text": "Changes allowed up to 2 hours before reservation"
  }
}

Important Notes

  • book_token.value is the real encrypted booking token
  • Book token expires in ~3 minutes (check date_expires)
  • Response includes user's payment methods
  • Shows cancellation policy and fees
  • This token must be used immediately for booking

4. Book Reservation

Final step: Actually book the reservation.

Request

POST https://api.resy.com/3/book
Content-Type: application/x-www-form-urlencoded
Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"
X-Resy-Auth-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
X-Resy-Universal-Auth: eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Origin: https://resy.com
Referer: https://resy.com/

book_token=7TWgoK_Vi5aSUfHvc6pN%7CjXBy9PG_lcc13e7pgMAYeLAPKKyqs_G2PrGSi4X32SK...&struct_payment_method=%7B%22id%22%3A31876445%7D&source_id=resy.com-venue-details&venue_marketing_opt_in=0

Request Body (URL-encoded)

Field Type Required Description
book_token string Yes Encrypted token from /3/details
struct_payment_method string Yes* JSON object: {"id": 31876445}
source_id string Yes Always "resy.com-venue-details"
venue_marketing_opt_in int Yes 0 or 1 (marketing emails)
rwg_token string No For special/high-demand reservations
merchant_changed int No Always 1 if present

* Required for most restaurants with cancellation fees

Response (Success - 201 Created)

{
  "resy_token": "wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ_slhyCRooDsTBUWa1KvVhQyxVoK5...",
  "confirmation": "ABC123XYZ",
  "payment_method": {
    "id": 31876445
  }
}

Response (Error - 400 Bad Request)

{
  "status": 400,
  "message": "Invalid data received.",
  "data": {
    "book_token": "invalid"
  }
}

Common Errors

Error Cause Solution
invalid book token Token expired or incorrect Get fresh token from /3/details
payment method required Missing payment method Add credit card to account
slot no longer available Someone else booked it Start over from /4/find
unauthorized Invalid auth token Refresh auth token

Critical Notes

  • ⚠️ Content-Type MUST be application/x-www-form-urlencoded (NOT JSON!)
  • ⚠️ struct_payment_method is a JSON string inside the form data
  • ✅ On success, returns resy_token (different from auth token)
  • ✅ Check your email for confirmation
  • ⏱️ Book token expires in ~3 minutes

Additional Endpoints

Get Venue Calendar

See multi-day availability overview.

GET https://api.resy.com/4/venue/calendar?venue_id=6194&num_seats=2&start_date=2025-10-16&end_date=2025-10-30

Response:

{
  "scheduled": [
    {
      "date": "2025-10-16",
      "inventory": {
        "reservation": "available"
      }
    },
    {
      "date": "2025-10-17",
      "inventory": {
        "reservation": "sold_out"
      }
    }
  ]
}

Get User Profile

GET https://api.resy.com/2/user
X-Resy-Auth-Token: ...

Get User Reservations

GET https://api.resy.com/3/user/reservations?limit=10
X-Resy-Auth-Token: ...

Cancel Reservation

POST https://api.resy.com/3/cancel
Content-Type: application/json

{
  "resy_token": "wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ..."
}

Data Types

Venue ID

{
  "id": {
    "resy": 6194
  }
}

Date Format

  • API expects: YYYY-MM-DD (e.g., 2025-10-16)
  • Timestamps: YYYY-MM-DD HH:MM:SS (e.g., 2025-10-16 18:15:00)

Price Range

  • 1 = $ (Under $25)
  • 2 = $$ ($25-$50)
  • 3 = $$$ ($50-$75)
  • 4 = $$$$ ($75+)

Slot Types

Common values for config.type:

  • Dining Room
  • Bar Counter
  • Patio
  • Chef's Counter
  • Private Dining Room

Error Handling

HTTP Status Codes

Code Meaning Action
200 Success Continue
201 Created (booking success) Reservation confirmed
400 Bad Request Check request format
401 Unauthorized Refresh auth token
404 Not Found Venue/slot doesn't exist
429 Rate Limited Wait and retry
500 Server Error Retry after delay

Common Error Patterns

Invalid Book Token

{
  "message": {
    "book_token": "An invalid book token was provided."
  }
}

→ Token expired or incorrect. Get fresh token from /3/details.

Slot No Longer Available

{
  "message": "Slot is no longer available"
}

→ Someone else booked it. Start over from /4/find.

Payment Method Required

{
  "message": "Payment method required for this reservation"
}

→ Add credit card to Resy account, then pass payment_method_id.


Best Practices

1. Token Management

# ❌ BAD: Reuse old token
token = get_token_from_availability()
time.sleep(300)  # 5 minutes
book(token)  # Will fail - token expired

# ✅ GOOD: Get fresh token immediately before booking
availability = check_availability()
details = get_booking_details(availability.config_token)  # Fresh token
book(details.book_token)  # Immediately

2. Rate Limiting

# ✅ GOOD: Respect rate limits
import time

for venue in venues:
    check_availability(venue)
    time.sleep(0.5)  # 500ms between requests

3. Concurrent Monitoring

// ✅ GOOD: Check multiple venues simultaneously
venues := []int{6194, 78062, 65230}
results := make(chan Slots, len(venues))

for _, venueID := range venues {
    go func(id int) {
        slots, _ := client.CheckAvailability(ctx, id, date, partySize)
        results <- slots
    }(venueID)
}

4. Error Recovery

# ✅ GOOD: Handle transient failures
def book_with_retry(venue_id, date, party_size, max_retries=3):
    for attempt in range(max_retries):
        try:
            slots = check_availability(venue_id, date, party_size)
            if not slots:
                return None
            
            details = get_booking_details(slots[0].config_token, date, party_size)
            booking = book_reservation(details.book_token, payment_method_id)
            return booking
        except BookingError as e:
            if "no longer available" in str(e):
                continue  # Retry
            raise  # Other errors
    return None

5. Complete Headers

Always include browser-like headers to avoid being flagged:

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Origin: https://resy.com
Referer: https://resy.com/
Accept: application/json, text/plain, */*

6. Time Windows

  • Book tokens expire in ~3 minutes
  • Auth tokens expire in ~45 days
  • High-demand restaurants: book within seconds of availability

Complete Example: Book a Reservation

import requests
import time

API_KEY = "YOUR_RESY_API_KEY"
AUTH_TOKEN = "your_auth_token_here"

headers = {
    "Authorization": f'ResyAPI api_key="{API_KEY}"',
    "X-Resy-Auth-Token": AUTH_TOKEN,
    "X-Resy-Universal-Auth": AUTH_TOKEN,
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
    "Origin": "https://resy.com",
    "Referer": "https://resy.com/"
}

# Step 1: Search
search_resp = requests.post(
    "https://api.resy.com/3/venuesearch/search",
    json={"query": "carbone"},
    headers={"Authorization": headers["Authorization"]}
)
venue_id = search_resp.json()["search"]["hits"][0]["id"]["resy"]

# Step 2: Check Availability
availability_resp = requests.get(
    "https://api.resy.com/4/find",
    params={
        "venue_id": venue_id,
        "day": "2025-10-16",
        "party_size": 2,
        "lat": 0,
        "long": 0
    },
    headers=headers
)
slots = availability_resp.json()["results"]["venues"][0]["slots"]
config_token = slots[0]["config"]["token"]

# Step 3: Get Booking Details (CRITICAL!)
details_resp = requests.post(
    "https://api.resy.com/3/details",
    json={
        "config_id": config_token,
        "day": "2025-10-16",
        "party_size": 2
    },
    headers={**headers, "Content-Type": "application/json"}
)
book_token = details_resp.json()["book_token"]["value"]
payment_method_id = details_resp.json()["user"]["payment_methods"][0]["id"]

# Step 4: Book (IMMEDIATELY - token expires fast!)
booking_resp = requests.post(
    "https://api.resy.com/3/book",
    data={
        "book_token": book_token,
        "struct_payment_method": f'{{"id":{payment_method_id}}}',
        "source_id": "resy.com-venue-details",
        "venue_marketing_opt_in": 0
    },
    headers={**headers, "Content-Type": "application/x-www-form-urlencoded"}
)

if booking_resp.status_code == 201:
    print(f"✅ Booked! Resy Token: {booking_resp.json()['resy_token']}")
else:
    print(f"❌ Failed: {booking_resp.text}")

Appendix: Token Format Examples

Config Token (from /4/find)

rgs://resy/6194/1521665/2/2025-10-16/2025-10-16/18:15:00/2/Bar Counter

Format: rgs://resy/{venue_id}/{config_id}/{party_size}/{start_date}/{end_date}/{time}/{party_size}/{type}

Book Token (from /3/details)

7TWgoK_Vi5aSUfHvc6pN|jXBy9PG_lcc13e7pgMAYeLAPKKyqs_G2PrGSi4X32SK_bXga_j4qVHRl36595nUF...

Encrypted token, typically 800-1200 characters.

Resy Token (from /3/book)

wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ_slhyCRooDsTBUWa1KvVhQyxVoK5...

Reservation identifier, used for cancellation/modification.


Documentation Version: 1.0
Last Updated: October 2025
Maintained by: Reverse Engineering Research

⚠️ Disclaimer: This is unofficial documentation. Resy may change their API at any time. Use responsibly and respect their terms of service.