# Resy Availability Checker: Open Tables by Date & Party Size (`superslowsloth/resy-availability`) Actor

Read-only Resy table availability: paste restaurant URLs, pick dates and a party size, get one row per restaurant per date with Resy's calendar status and every open slot (time, seating type). Schedule it to alert when a hard-to-book table opens. Never books.

- **URL**: https://apify.com/superslowsloth/resy-availability.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** Travel, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.69 / 1,000 availability checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Resy Availability Checker: Open Tables by Date & Party Size

Paste the restaurants you cannot get into, pick the dates and a party size, and
get one row per restaurant per date from [Resy](https://resy.com): Resy's own
calendar status for a reservation (`available`, `sold-out`, `closed`), the
**list of open slots** (start, end, seating type) and the date it was read.
Schedule it every few minutes with a webhook on **Only dates with open tables**
and it becomes a **cancellation alert** for hard-to-book tables.

**Read-only.** This actor only reads availability. It never books, holds or
cancels a table, needs no Resy account, and does not log in. It leaves out the
one value Resy puts on each slot that exists to make a booking (see below).

No account, no API key, no browser. One calendar request per restaurant, plus
one availability request per date that is actually open.

### Input

| Field | Notes |
|---|---|
| `venues` | One restaurant per line: its resy.com page `https://resy.com/cities/ny/venues/lilia`, the short form `ny/lilia`, or Resy's numeric venue id `418`. The same restaurant given twice is checked once. |
| `partySize` | How many guests (1 to 30, default 2). Sent to Resy on every call; each row carries the restaurant's own minimum and maximum party, and the log warns when yours is outside them. |
| `dates` | Specific dates, `YYYY-MM-DD`, one per line, on the restaurant's own calendar. |
| `startDate`, `daysAhead` | Used when `dates` is empty: `daysAhead` consecutive days (default 14, up to 120) from `startDate`, which defaults to today **on each restaurant's own clock** (a New York restaurant at 10 pm Eastern is still on the day before in UTC). |
| `timeFrom`, `timeTo` | Only count slots starting inside this window, 24-hour `HH:MM` on the restaurant's clock, both ends inclusive. |
| `seatingTypes` | Only count slots in these areas, named as Resy names them (`Dining Room`, `Bar`, `Patio`, `Indoor`...), case ignored. |
| `onlyAvailable` | Keep only rows with at least one slot, so a run that finds nothing leaves an empty dataset. Every date is still checked and charged. |
| `maxItems` | Check at most this many restaurant-dates (default 500, up to 5,000). |
| `proxyConfiguration` | Apify **datacenter** proxy, the default. |

Example input, a weekend dinner alert:

```json
{
  "venues": [
    "https://resy.com/cities/ny/venues/lilia",
    "ny/carbone"
  ],
  "partySize": 2,
  "dates": ["2026-11-06", "2026-11-07", "2026-11-13", "2026-11-14"],
  "timeFrom": "18:00",
  "timeTo": "21:30",
  "seatingTypes": ["Dining Room", "Indoor"],
  "onlyAvailable": true
}
```

A line that is not a Resy restaurant, a date that is not `YYYY-MM-DD`, a date in
the past, or a time that is not `HH:MM` stops the run **before anything is
charged**, with the line named.

### Output

One row per restaurant per date. Real values from 2026-10-20, `slots` shortened
to two of 14:

```json
{
  "query": "https://resy.com/cities/ny/venues/lilia",
  "venue_id": 418,
  "venue_name": "Lilia",
  "venue_slug": "lilia",
  "venue_url": "https://resy.com/cities/new-york-ny/venues/lilia",
  "cuisine": "Italian",
  "city": "Brooklyn",
  "neighborhood": "Williamsburg",
  "region": "NY",
  "city_code": "ny",
  "time_zone": "EST5EDT",
  "min_party_size": 2,
  "max_party_size": 6,
  "date": "2026-10-20",
  "party_size": 2,
  "calendar_status": "available",
  "walk_in_status": "available",
  "event_status": "not available",
  "available": true,
  "slots_checked": true,
  "slot_count": 14,
  "slots": [
    {"start": "2026-10-20 17:00:00", "end": "2026-10-20 19:00:00", "type": "Bar", "party_min": 2, "party_max": 2, "is_paid": false},
    {"start": "2026-10-20 17:00:00", "end": "2026-10-20 19:00:00", "type": "Indoor", "party_min": 2, "party_max": 2, "is_paid": false}
  ],
  "checked_at": "2026-10-20T16:00:00Z"
}
```

| Field | Notes |
|---|---|
| `query`, `venue_id`, `venue_name`, `venue_slug`, `venue_url` | The line you gave and the restaurant Resy resolved it to. |
| `cuisine`, `city`, `neighborhood`, `region`, `city_code`, `time_zone` | From Resy's venue record. `city` is Resy's locality (Lilia is in Brooklyn although Resy files it under New York), `city_code` is Resy's short code (`ny`). |
| `min_party_size`, `max_party_size` | The restaurant's own bounds. A party outside them gets no slots. |
| `date`, `party_size` | What was checked. |
| `calendar_status` | Resy's word for a reservation on that date, as sent: `available`, `sold-out`, `closed` or `not available` (event-only dates). **`not-scheduled`** is this actor's word, not Resy's: the date is not on the restaurant's calendar at all (not released yet, or past its booking window). |
| `walk_in_status`, `event_status` | Resy's words for walk-in and event seating on that date, as sent; null when the date is not on the calendar. A sold-out date can still list walk-ins. |
| `available` | `true` when `slots` holds at least one slot after your time and seating filters. The field to alert on. |
| `slots_checked` | Whether the availability call was made for this date (see below). An empty `slots` is never ambiguous. |
| `slot_count`, `slots` | Each slot: `start` and `end` as Resy sends them (the restaurant's local clock, no zone, `YYYY-MM-DD HH:MM:SS`), `type` (the seating area), `party_min` and `party_max` (the parties the slot seats), and `is_paid` (Resy's flag for a slot that takes a payment or deposit; null when absent). |
| `checked_at` | When the check ran, UTC. |

#### What is left out, and why

Each slot Resy returns carries a `config.token` (`rgs://resy/...`). It is the
handle the booking call takes for that exact slot. This actor only reads
availability, so that **booking token** is not copied into the dataset: a dataset
is often shared, exported and posted to webhooks, and nothing in an availability
alert needs the one value that exists to make a reservation.

### How it reads Resy, and the calendar shortcut

For each restaurant: one request resolves it (name, city, time zone, party
bounds), one request reads its calendar for the whole date range, and the
availability call is made only for dates the calendar calls `available`.

Measured 2026-10-20 on three restaurants, comparing the calendar with Resy's
availability answer for the same date: every `sold-out` date (5 of 5) came back
with no slots, every `closed` date (2 of 2) came back with no venue at all, the
one event-only date (`reservation` `not available`) came back with no slots, and
every `available` date (3 of 3) had slots. So a sold-out or closed date costs one
cheap calendar entry instead of a 13 to 39 KB availability answer, and the log
says how many availability reads were skipped. A calendar word the actor has not
seen before is read with the availability call rather than guessed at.

Resy only lists the dates a restaurant has scheduled: Carbone's calendar on
2026-10-20 ran to 2026-11-18 plus two event-only dates, and Lilia's to 2026-11-15
(27 dates). The dates in between and after are `not-scheduled`. The restaurants'
booking windows ran from 19 to 66 days in the three measured, so a high
`daysAhead` is mostly `not-scheduled` rows; they are still checked, and charged,
like any other date.

### Pricing

Pay per event: **$0.00069 per date checked** plus **$0.002 per run start**. One
date at one restaurant is one check, whatever its status: a sold-out date costs
the same as a date with twenty slots, because "nothing is open" is the answer an
alert is waiting for, and the price is knowable before the run (restaurants times
dates). 2 restaurants over 14 days is 28 checks, $0.021. `onlyAvailable` changes
which rows are kept, not what is charged. A restaurant Resy does not have, a date
whose availability could not be read after retries, a duplicate line, or a date
past `maxItems` is not charged.

### Where it runs, and what the API key is

Nothing here is scraped from a rendered page: it calls the same JSON API that
resy.com's own page calls, using the public web key that resy.com's JavaScript
embeds (`apiKey` in `modules/app.<hash>.js`). It is not a secret and not tied to
an account. Resy can rotate it; when it does, every call answers HTTP 419
`Unauthorized`. The actor then reads the current key from resy.com's own bundle
once, logs that it did, and goes on. If no different key can be read the run
fails with a message saying the key was rejected, instead of returning empty
rows.

Measured 2026-10-20: Apify datacenter addresses answered the calendar, venue and
availability calls 9 of 9 (HTTP 200); the Apify residential proxy answered 4 of
5, one connection reset. The default is the automatic **datacenter** proxy. From
a single non-Apify address, the availability call began answering an empty HTTP
500 after about 45 requests in 45 minutes while the venue and calendar calls kept
answering, so a refused request (403, 429, 5xx, an empty or non-JSON 200) is
treated as that address being blocked: the actor rotates to a fresh address and
retries before giving up on that date.

One header detail, measured the same day: the calendar call answers an empty HTTP
500 when the request carries a `Referer` header (3 of 3), and answers 200 when it
does not (3 of 3). The actor sends the key and `Origin` only.

Limits worth knowing:

- Slot times are the restaurant's local clock with no zone. `time_zone` is
  Resy's zone name for the restaurant.
- The availability call is per party size; a party the restaurant cannot seat
  gets no slots even on an open date.
- Some venues sell tickets, prix-fixe seatings or events outside the standard
  reservation flow; those show up as the `event_status`, not as `slots`.
- If Resy changes its answer's shape the run fails that date with a message
  instead of returning empty rows.

# Actor input Schema

## `venues` (type: `array`):

One restaurant per line: its resy.com page (https://resy.com/cities/ny/venues/lilia), the short form city/venue (ny/lilia), or Resy's numeric venue id (418). A restaurant Resy does not have is reported and not charged. The same restaurant given twice is checked once.

## `partySize` (type: `integer`):

How many guests to look for. Resy answers per party size, and a restaurant only lists tables that seat it (each row carries the restaurant's own minimum and maximum).

## `dates` (type: `array`):

Specific dates to check, as YYYY-MM-DD, one per line, on the restaurant's own calendar. Leave empty to check a run of days instead (Start date and Days ahead).

## `startDate` (type: `string`):

First date of the run of days, as YYYY-MM-DD. Leave empty to start today on each restaurant's own clock. Ignored when Dates is filled in.

## `daysAhead` (type: `integer`):

How many consecutive days to check from the start date (the restaurants' booking windows measured at build time ran from 19 to 66 days; dates beyond a window come back as not-scheduled). Ignored when Dates is filled in.

## `timeFrom` (type: `string`):

Only count slots that start at or after this time, 24-hour HH:MM on the restaurant's own clock (for example 18:00). Leave empty for no limit.

## `timeTo` (type: `string`):

Only count slots that start at or before this time, 24-hour HH:MM on the restaurant's own clock (for example 21:30). Leave empty for no limit.

## `seatingTypes` (type: `array`):

Only count slots in these areas, named as Resy names them (Dining Room, Bar, Patio, Indoor...). Matching ignores case. Leave empty for every area; the area names a restaurant uses are in the slots of an unfiltered run.

## `onlyAvailable` (type: `boolean`):

Keep only the rows that have at least one open slot, so a run that finds nothing leaves an empty dataset - the shape an alert or webhook wants. Every date is still checked and charged, because the check is the work.

## `maxItems` (type: `integer`):

Check at most this many restaurant-dates in one run (restaurants times dates), in input order. The rest are not read or charged.

## `proxyConfiguration` (type: `object`):

Use Apify's automatic datacenter proxy (the default). api.resy.com answered 9 of 9 requests from Apify datacenter addresses when this actor was built. A single address was refused (empty HTTP 500) on the availability call after about 45 requests in 45 minutes, so a refused request is retried from a fresh address.

## Actor input object example

```json
{
  "venues": [
    "https://resy.com/cities/ny/venues/lilia",
    "https://resy.com/cities/ny/venues/carbone"
  ],
  "partySize": 2,
  "dates": [],
  "daysAhead": 14,
  "seatingTypes": [],
  "onlyAvailable": false,
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "venues": [
        "https://resy.com/cities/ny/venues/lilia",
        "https://resy.com/cities/ny/venues/carbone"
    ],
    "dates": [],
    "seatingTypes": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/resy-availability").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "venues": [
        "https://resy.com/cities/ny/venues/lilia",
        "https://resy.com/cities/ny/venues/carbone",
    ],
    "dates": [],
    "seatingTypes": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/resy-availability").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "venues": [
    "https://resy.com/cities/ny/venues/lilia",
    "https://resy.com/cities/ny/venues/carbone"
  ],
  "dates": [],
  "seatingTypes": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/resy-availability --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/resy-availability"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kXhooA7OWVSr3AR23/builds/8o1nM6lutzEnq9269/openapi.json
