Reverse Engineering Resy’s API to Book the Most Exclusive Restaurants in Seconds

n8te
5 min readDec 2, 2022

--

Getting Started

Resy.com is an online platform that allows users to book reservations at food venues across the globe. Resy has quickly risen to the top of the online food services, seating 2.6 million users every week. Here is how I used their own website against them to book a reservation wherever, whenever I wanted.

Before writing any code, I needed to understand how Resy’s website worked. The best way to do this was to open DevTools (Ctrl + Shift + C) and navigate to the “Network” tab. On this tab, I can view requests being sent to and from the website. Furthermore, hidden requests containing data called endpoints become visible. After shadowing the process of booking a reservation with my network tab opened, I had all the information I needed to begin understanding and reversing Resy’s API to use it against them.

Creating the Bot

Looking at the network activity I collected, I noticed that every request sent to the website had three authenticity tokens attached in the request headers: x-resy-auth-token ,x-resy-universal-auth and authorization. Removing or altering the values of these tokens resulted in a 419 error code meaning the user’s session had expired. Because all three of the tokens’ values only changed when I logged out and back in on a new account, I concluded that their function was to verify that a valid user was logged into the site. This turned out to be correct as I simply imported the tokens’ values into a GET request sent to the website’s home page and was logged into my account after the request was sent. This not only meant that I could skip logging in entirely, but that all I needed to do to authenticate my requests was to pass in the token values into the request header as constants.

Now that I was able to freely interact with the Resy, I could start making a reservation. After selecting a date, party size, and restaurant on the site, a GET request https://api.resy.com/4/find?lat=0&long=0&day=2022–08–01&party_size=2&venue_id=5286z was made returning data that contained all available reservations paired with a config_idthat will come in handy later. By converting the response into a json object and parsing it, I made clean lists of both values:

I could now check the availability of any restaurant I wanted by changing the search queries. Lat and long were set to zero, so I ignored them. Both dateand party_size are self explanatory fields, but what is the venue_id? I discovered that the venue_idof any restaurant is located in the URL of an image on their page’s HTML. I put it below in green.

HTML block for image on restaurant’s page w/ venue_id

Now I could make a GET request to the endpoint for any restaurant. The date, party_size, and venue_idvariables could be changed as desired.

GET request with queries as variables w/ desired values

The next endpoint in the process of booking a reservation was https://api.resy.com/3/details, a POST request which must be passed a config_id (which was returned from the previous endpoint). Each open reservation was paired with a singleconfig_id , so I needed to select only one timeslot now. I did that by selecting a random index from the configIDs list returned from the previous endpoint:

However, if I wanted a specific timeslot rather than a random one, I could input a specific time and assign configID a value once that time’s index is passed:

In the response of the /detailsPOST request was a unique value: book_token. I scraped this value from the json object and saved it in a variable “id”:

POST request for /details endpoint

The final step for completing this reservation was to simply pass this book_token value into one last endpoint:https://api.resy.com/3/book. However, this endpoint required another field I had not seen before called struct-payment-method with an integer value tied to it. Just like the tokens, this value was static so I concluded, again, it was connected to the account. This meant that the value could be inserted directly into the payload with no alterations.

I located the struct-payment-methodby making a GET request to the endpoint tied to a user’s account, https://api.resy.com/2/userand, sure enough, a payment_id was in the json object. All that was left to do was scrape the struct-payment-methodfrom the /user endpoint and pass it into the final /bookendpoint.

POST request for the /book endpoint

After sending the final request, I checked the “my reservations” tab on the Resy website and saw that my script had worked; the reservation went through.

I then added a monitoring feature that checked if the list of available times was empty. If it wasn’t empty, the script would run. Otherwise, it would wait 5 seconds and check again:

There we have it! A fully functioning Resy bot to snag up reservations at the most exclusive spots before anyone else can.

--

--