{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenTable Scraper — Restaurants, Ratings & Demand",
    "description": "Search OpenTable anywhere in the world for each restaurant's full record — address, phone, coordinates, neighbourhood, cuisine, price band, rating, review count and photos — plus OpenTable's own recent-reservation count, a direct measure of how busy a restaurant is. Unofficial.",
    "version": "0.1",
    "x-build-id": "SUsXpRbNGSxzqkon5"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/simple.actors~opentable-restaurants/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-simple.actors-opentable-restaurants",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/simple.actors~opentable-restaurants/runs": {
      "post": {
        "operationId": "runs-sync-simple.actors-opentable-restaurants",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/simple.actors~opentable-restaurants/run-sync": {
      "post": {
        "operationId": "run-sync-simple.actors-opentable-restaurants",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "term"
        ],
        "properties": {
          "term": {
            "title": "Search term",
            "type": "string",
            "description": "What to search OpenTable for — a restaurant name (\"Balthazar\") or a cuisine (\"steak\"). If you set \"Restaurant IDs\" below and leave this at its default, no search is run and only those IDs are read.",
            "default": "steak"
          },
          "city": {
            "title": "City",
            "type": "string",
            "description": "Where to search — a city, a town or a neighbourhood, written the way you would type it into OpenTable's own location box (\"Miami\", \"Brooklyn\", \"Napa\", \"Sydney\"). It is matched against OpenTable's own directories, so the search lands where OpenTable puts that place rather than where some other map does, and the run log names what it matched along with anything else that was close. Add a state or country the way OpenTable writes them when a name is ambiguous — \"Cambridge, MA\", \"London, Canada\". Needs no session or cookies. Leave it empty and set \"Latitude\"/\"Longitude\" instead if you would rather name the point yourself; give neither and OpenTable places the search by the address the proxy happens to use, which is not something a run controls."
          },
          "state": {
            "title": "State or country",
            "type": "string",
            "description": "Only needed when a city name is ambiguous. Leave it empty first: \"Miami\" and \"London\" resolve on their own, and on their own they search the whole market, which is usually what you want. If the run comes back saying several places share the name, put the state or country here exactly as OpenTable wrote it in that message — \"NY\", \"MA\", \"IL\", \"Canada\", \"Portugal\". You can also write it straight into \"City\" as \"Manhattan, NY\"; this field is the same thing with the comma spared."
          },
          "latitude": {
            "title": "Latitude",
            "type": "number",
            "description": "Optional. Anchor the search on an exact point instead of naming a place in \"City\". Both this and \"Longitude\" have to be set for either to be used, and together they override \"City\". Nothing is assumed when they are left empty — there is no built-in default location."
          },
          "longitude": {
            "title": "Longitude",
            "type": "number",
            "description": "See \"Latitude\". Coverage is global: OpenTable ranks by distance from this point wherever it is."
          },
          "restaurantIds": {
            "title": "Restaurant IDs",
            "type": "array",
            "description": "Read these OpenTable restaurant IDs directly instead of (or as well as) searching. Each one is read from its own OpenTable page, so you get the full record — name, address, coordinates, rating, review count, price band, photos, whether it is bookable, and the recent-reservation count — without running a separate search per restaurant. This is the mode for tracking a known set of venues on a schedule, and it is charged at the ordinary per-restaurant price. Turn on \"Read full details\" as well if you also want the website, amenities and opening hours.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxItems": {
            "title": "Maximum restaurants",
            "minimum": 1,
            "type": "integer",
            "description": "Cap on how many search results to keep. The search page returns 50 either way and the whole cost of a run is fetching it, so lowering this saves you almost nothing while raising it costs almost nothing — the per-restaurant price is the cheap part. Leave it at 50 unless you specifically want a short list.",
            "default": 50
          },
          "includeDetails": {
            "title": "Read full details",
            "type": "boolean",
            "description": "Adds the restaurant's website, its full amenity list, opening hours, executive chef, dress code, parking, payment options, every cuisine, country, review counts, recent review text, popular dishes and special menus — everything that lives only on the restaurant's own OpenTable page. For search results this costs one extra page load PER RESTAURANT, where the search itself costs one load for fifty, so it is charged separately and is much slower. For \"Restaurant IDs\" that page is being read anyway, so this switch only decides whether you are charged for the extra fields.",
            "default": false
          },
          "includeAvailability": {
            "title": "Read bookable slots (needs session cookies)",
            "type": "boolean",
            "description": "Adds every open reservation time around \"Time\" for the given date and party size. REQUIRES \"Session cookies\" below — OpenTable answers this endpoint only for a browser session that is already valid. Turn this on without one and the run still succeeds and still returns every restaurant record; each row then carries \"availabilityError\" saying what was missing, and no slot fields at all, so a missing answer is never mistaken for a fully booked restaurant.",
            "default": false
          },
          "date": {
            "title": "Date",
            "type": "string",
            "description": "The day to read availability for, as YYYY-MM-DD. Required when \"Read bookable slots\" is on."
          },
          "time": {
            "title": "Time",
            "type": "string",
            "description": "The time to centre the slot search on, as HH:MM (24-hour). Slots are returned for three hours either side, which covers a whole dinner service in one request.",
            "default": "19:00"
          },
          "partySize": {
            "title": "Party size",
            "minimum": 1,
            "type": "integer",
            "description": "Number of diners. Availability is per party size — a table for 2 and a table for 8 are different questions.",
            "default": 2
          },
          "primeWindowStart": {
            "title": "Prime window start",
            "type": "string",
            "description": "Start of the window scored by \"primeWindowOpenPercent\", as HH:MM. The default 18:00-21:00 is the dinner peak; move it to score a lunch service instead.",
            "default": "18:00"
          },
          "primeWindowEnd": {
            "title": "Prime window end",
            "type": "string",
            "description": "End of the scored window, as HH:MM. Openness is normalised by each restaurant's own booking granularity, so a venue booking on the half hour is not scored as half empty against one booking on the quarter hour.",
            "default": "21:00"
          },
          "sessionCookies": {
            "title": "Session cookies",
            "type": "string",
            "description": "REQUIRED for availability. OpenTable's reservation endpoint is behind Akamai and answers only a browser session that is already valid, so this Actor uses one you supply rather than manufacturing one. To get it: open opentable.com in your browser, open DevTools > Network, search any city, click a request to /dapi/fe/gql, and copy the whole \"cookie\" request header. A jar stays usable for roughly 40 minutes — but restaurants are batched 10 per request, so one jar covers thousands of them. Leave empty if you only want restaurant records."
          },
          "userAgent": {
            "title": "User agent",
            "type": "string",
            "description": "Must name the same browser the cookies came from. A mismatch between this and the client-hint headers is exactly what the bot filter looks for, so only change it if you also change \"secChUa\" to agree."
          },
          "secChUa": {
            "title": "sec-ch-ua header",
            "type": "string",
            "description": "The client-hint brand list your browser sends. This header is load-bearing: an otherwise identical request without it is refused. Copy it from the same DevTools request as the cookies if the default stops working."
          },
          "secChUaPlatform": {
            "title": "sec-ch-ua-platform header",
            "type": "string",
            "description": "The platform your cookies were minted on, quoted — \"Windows\", \"macOS\" or \"Linux\". Should agree with \"userAgent\"."
          },
          "csrfToken": {
            "title": "CSRF token",
            "type": "string",
            "description": "Optional. The x-csrf-token your browser sent alongside the cookies. Supply it if availability starts being refused with a valid-looking session."
          },
          "databaseRegion": {
            "title": "Database region",
            "type": "string",
            "description": "Which OpenTable regional backend to ask. \"NA\" covers North America and is the tested default; try \"EMEA\" or \"APAC\" if availability comes back empty for restaurants that clearly have tables.",
            "default": "NA"
          },
          "includeRaw": {
            "title": "Include raw payloads",
            "type": "boolean",
            "description": "Attaches OpenTable's untouched JSON under \"raw\" and \"rawAvailability\". Turn on when you need a field this Actor does not map yet.",
            "default": false
          },
          "proxy": {
            "title": "Proxy",
            "type": "object",
            "description": "Apify Proxy settings. OpenTable refuses datacenter exit IPs on its HTML with a 403, so the default asks for the UNBLOCKER group; switch to RESIDENTIAL if your plan carries it. A plan with neither quietly falls back to datacenter, and the run will fail with a 403 that says so.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "UNBLOCKER"
              ]
            }
          },
          "detailProxy": {
            "title": "Profile page proxy",
            "type": "object",
            "description": "Proxy for reading individual restaurants' OpenTable pages, which — unlike the search page — are served to ordinary datacenter addresses. Leaving this at its default is what keeps reading by ID cheap. If profile pages start being refused the Actor falls back to \"Proxy\" above on its own; point this at the Unblocker only if that keeps happening.",
            "default": {
              "useApifyProxy": true
            }
          },
          "availabilityProxy": {
            "title": "Availability proxy",
            "type": "object",
            "description": "Proxy for the reservation lookup specifically, which needs residential egress. Datacenter addresses are not just slower here, they are refused: the connection is dropped before your cookies are looked at. It also has to be separate from the main proxy, because the Unblocker that reads the search page carries GET requests only and answers this POST with a timeout and then a 502.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}