Complete documentation for the Resy booking API, reverse-engineered from production traffic.
Resy uses two authentication mechanisms:
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
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
Option 1: From Browser (Recommended)
- Open Resy website and log in
- Open DevTools → Network tab
- Find any
api.resy.comrequest - Copy the
X-Resy-Auth-Tokenheader 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=yourpasswordResponse:
{
"auth_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9...",
"payment_method_id": "12345678"
}┌─────────────────────────────────────────────────────────────┐
│ 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"
Find restaurants by name, cuisine, or keyword.
POST https://api.resy.com/3/venuesearch/search
Content-Type: application/json
Authorization: ResyAPI api_key="YOUR_RESY_API_KEY"
{
"query": "carbone"
}{
"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
}
}
]
}
}- No authentication required (public search)
- Returns up to 50 results
- Price range: 1 (
$), 2 ($ $), 3 ($$$), 4 ($$$$)
Get available reservation slots for a specific venue, date, and party size.
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/| 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) |
{
"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"
}
}
]
}
]
}
}⚠️ Theconfig.tokenis NOT the booking token- This token must be passed to
/3/detailsto get the real booking token - Slots may disappear quickly (high-demand restaurants)
config.typeindicates seating type (Dining Room, Bar, Patio, etc.)
Critical step: Converts config token to real booking token.
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
}| 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 |
{
"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"
}
}- ✅
book_token.valueis 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
Final step: Actually book the reservation.
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| 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
{
"resy_token": "wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ_slhyCRooDsTBUWa1KvVhQyxVoK5...",
"confirmation": "ABC123XYZ",
"payment_method": {
"id": 31876445
}
}{
"status": 400,
"message": "Invalid data received.",
"data": {
"book_token": "invalid"
}
}| 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 |
⚠️ Content-Type MUST beapplication/x-www-form-urlencoded(NOT JSON!)⚠️ struct_payment_methodis 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
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-30Response:
{
"scheduled": [
{
"date": "2025-10-16",
"inventory": {
"reservation": "available"
}
},
{
"date": "2025-10-17",
"inventory": {
"reservation": "sold_out"
}
}
]
}GET https://api.resy.com/2/user
X-Resy-Auth-Token: ...GET https://api.resy.com/3/user/reservations?limit=10
X-Resy-Auth-Token: ...POST https://api.resy.com/3/cancel
Content-Type: application/json
{
"resy_token": "wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ..."
}{
"id": {
"resy": 6194
}
}- 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)
- 1 = $ (Under $25)
- 2 = $$ ($25-$50)
- 3 = $$$ ($50-$75)
- 4 = $$$$ ($75+)
Common values for config.type:
Dining RoomBar CounterPatioChef's CounterPrivate Dining Room
| 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 |
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.
# ❌ 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# ✅ GOOD: Respect rate limits
import time
for venue in venues:
check_availability(venue)
time.sleep(0.5) # 500ms between requests// ✅ 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)
}# ✅ 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 NoneAlways 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, */*- Book tokens expire in ~3 minutes
- Auth tokens expire in ~45 days
- High-demand restaurants: book within seconds of availability
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}")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}
7TWgoK_Vi5aSUfHvc6pN|jXBy9PG_lcc13e7pgMAYeLAPKKyqs_G2PrGSi4X32SK_bXga_j4qVHRl36595nUF...
Encrypted token, typically 800-1200 characters.
wF4wAvd6kAYiXDjhFqrhOoUsTlKKolFgwarZ_slhyCRooDsTBUWa1KvVhQyxVoK5...
Reservation identifier, used for cancellation/modification.
Documentation Version: 1.0
Last Updated: October 2025
Maintained by: Reverse Engineering Research