For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Primary navigation
Overview ChatGPT + Codex user docs Use cases

Hotel property feeds (limited beta)

Create and upload hotel property feeds for the limited Hotel ads beta.

Limited beta: Hotel property feeds are available only to approved pilot advertisers. This is not an open beta. Field definitions, validation rules, and supported capabilities may change during the beta.

Create a Hotel feed in Ads Manager or through the Ads API, then upload a full snapshot of your properties. Each row describes one property, not a room, offer, or dated stay. This is the hotel_property_v1 format; don’t use the Product feed schema for a Hotel feed.

Before you start

You need an ad account approved for the limited beta and permission to manage its feeds. SFTP setup also requires permission to manage feed credentials. Prepare a UTF-8 CSV or tab-delimited TXT file using the submission format, and replace example URLs with your property pages and images.

Choose a setup method:

Method Create the feed Send the file
Ads Manager Select Hotels when creating a feed. Upload CSV or TXT, configure a hosted URL, or connect over SFTP.
Public Ads API Create a feed with the Hotel inventory type and schema profile. Configure SFTP access through the API, then transfer the file over SFTP.

Hosted URL setup and browser file upload are Ads Manager workflows; their UI endpoints aren’t part of the public API instructions here. If Hotel options aren’t available for your account, contact your OpenAI account team.

Create and upload in Ads Manager

  1. Open Ads Manager and select the approved ad account.

  2. Open Feeds, select Create Feed, and choose Hotels as the inventory type.

  3. Enter a feed name, continue to the upload-method selection, and choose Upload CSV or TXT, Hosted URL, or SFTP connection.

  4. Create the feed and complete the selected source setup:

    Source Setup
    Upload CSV or TXT Select your full-snapshot file, review the preview, and submit the upload.
    Hosted URL Enter the HTTPS file URL and, if required, HTTP Basic authentication credentials. Save the URL, then start a manual fetch or configure an available automatic refresh schedule.
    SFTP connection Generate a password or configure an SSH public key. Connect an SFTP client using the displayed connection details and upload the file.
  5. Check Upload History for processing status and row diagnostics. Fix rejected rows and upload a corrected full snapshot.

Use the existing feed’s actions to upload or refresh later snapshots; don’t create a new feed for every update. Keep hotel_id values stable.

Create and upload through the Ads API

Use an ad account-scoped Advertiser API key with permission to manage the approved account’s feeds. Keep it on your server and provide it through OPENAI_ADS_API_KEY. See Authentication.

1. Create the Hotel feed

Set both inventory_type and schema_profile explicitly. Omitting the Hotel selectors doesn’t create a Hotel feed.

curl -X POST "https://api.ads.openai.com/v1/feeds" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: hotel-feed-setup-001" \
  -d '{
    "name": "Harbor House hotels",
    "inventory_type": "hotel",
    "schema_profile": "hotel_property_v1"
  }'

Replace the example Idempotency-Key with a unique value for this feed-creation operation. Reuse that key and the same request body when retrying the operation; use a new key when creating a different feed. Hotel feed creation requires this header.

Save the returned feed_id. Replace fd_123 in the following request with that ID.

2. Configure SFTP access

To generate password credentials:

curl -X POST "https://api.ads.openai.com/v1/feeds/fd_123/sftp_access" \
  -H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "authentication_method": "password"
  }'

Use the returned connection_uri and password in your SFTP client. Store the password securely; generating another password replaces the previous one.

For SSH-key authentication, send this body to the same endpoint instead, replacing YOUR_SSH_PUBLIC_KEY with the contents of your public-key file:

{
  "authentication_method": "ssh_key",
  "ssh_public_key": "YOUR_SSH_PUBLIC_KEY"
}

Use the corresponding private key in your SFTP client. Don’t send the private key to the API.

Credential setup replaces the existing authentication method: choosing a password removes configured SSH keys, and choosing an SSH key disables password authentication. Update any existing upload automation when you change credentials.

3. Transfer the file and check processing

Connect to the returned SFTP location. From your SFTP client’s prompt, upload your prepared full snapshot, for example:

put /path/to/hotels.csv hotels.csv

Place the file directly in the SFTP root, not a nested folder. File transfer and ingestion are separate: a successful upload doesn’t mean that every property was accepted or can serve in an ad. Check the Hotel feed’s Upload History in Ads Manager for processing results and diagnostics.

Submission format

Submit a full snapshot as UTF-8 CSV or tab-delimited TXT. Include the properties you intend to advertise in the pilot, and keep each hotel_id stable between uploads.

The format combines ordinary columns, flattened columns, and JSON-encoded cells:

Value CSV or TXT representation
Scalar fields Ordinary columns such as hotel_id, name, or base_price.
Images A JSON array in the images cell.
Location Flattened location.* columns, such as location.address.city.
Neighborhoods A JSON array in the location.neighborhoods cell.
Guest ratings Indexed, flattened guest_rating[n].* columns. Use the singular guest_rating prefix.
Ads metadata A JSON object in the ads_metadata cell.

Don’t submit top-level CSV columns named location or guest_ratings. Those names describe logical objects, not the accepted columns for CSV or TXT uploads. In CSV, enclose cells containing commas, quotation marks, or newlines in double quotation marks, and double each embedded quotation mark.

Example CSV

This example includes all required columns and explicitly enables Ads processing for the property. Replace the fictional property data and URLs before uploading. Your landing pages and images must be publicly accessible.

hotel_id,name,description,url,images,location.address.city,location.address.country_code,location.coordinates.latitude,location.coordinates.longitude,is_ads_eligible
hotel_001,Harbor House Hotel,A fictional waterfront hotel used to demonstrate the feed format.,https://example.com/hotels/hotel_001,"[{""url"":""https://placehold.co/800x600.jpg?text=Hotel+01"",""tags"":[""featured""]}]",San Francisco,US,37.789,-122.391,true

Download the ten-property example CSV for a sample that also includes brands, static prices, guest ratings, and categories. Its example.com landing pages and placehold.co images are placeholders, not properties to advertise.

Identity and descriptive fields

Required fields must have valid, nonempty values on every row, including rows with status set to archived.

Column Required Type and rules
hotel_id Yes Stable, case-sensitive string; up to 100 characters.
name Yes Display name; up to 150 characters. Must contain text, not only markup.
description Yes Static property description; up to 5,000 characters. Must contain text, not only markup.
url Yes Absolute HTTP or HTTPS property landing-page URL; up to 2,048 characters. Embedded credentials aren’t allowed.
brand No Hotel or chain brand; up to 70 characters. This is separate from the advertiser’s display identity.
status No active or archived. Defaults to active when omitted.
is_ads_eligible No A boolean. Set true for properties to process for ads, or false to opt out. If omitted, uses the feed’s configured default.

Leading and trailing whitespace is removed from text values. Display text is Unicode-normalized. Required strings can’t be empty or contain control characters. Blank optional scalar cells are treated as omitted.

Use is_ads_eligible in new files. The legacy is_eligible_ads field provides a fallback when the canonical field is absent; conflicting values reject the row. Eligibility doesn’t guarantee ad delivery: the property must also satisfy review and serving requirements.

Images

The images cell must contain a JSON array of 1–20 image objects. Invalid objects are removed if at least one valid image remains; a row with no valid image is rejected. Duplicate image URLs are removed, preserving order. The first remaining image is primary.

JSON field Required Type and rules
url Yes Absolute HTTP or HTTPS image URL; up to 2,048 characters. Embedded credentials aren’t allowed.
tags No Array of up to 20 strings, each 1–64 characters. Duplicate tags are removed, preserving order.

The JSON value before CSV escaping looks like this:

[
  {
    "url": "https://placehold.co/800x600.jpg?text=Hotel+01",
    "tags": ["featured"]
  }
]

Location

Include all four required location columns. A single location JSON cell doesn’t replace them. The country code describes the property’s physical location, not the countries where you want to show ads.

Column Required Type and rules
location.address.addr1 No Street address; up to 200 characters.
location.address.city Yes Display city; up to 100 characters.
location.address.region No State, province, or region; up to 100 characters.
location.address.postal_code No Postal code; up to 32 characters.
location.address.country_code Yes ISO 3166-1 alpha-2 country code, such as US. Normalized to uppercase.
location.coordinates.latitude Yes Finite number from -90 to 90, inclusive.
location.coordinates.longitude Yes Finite number from -180 to 180, inclusive.
location.neighborhoods No JSON array of up to 20 nonempty strings, each up to 100 characters. Duplicate values are removed, preserving order.

For example, the neighborhood value before CSV escaping is ["Waterfront"]. Neighborhoods are stored as property metadata.

Pricing and categorization

These fields describe the property independently of travel dates. base_price is a static starting price, not a quote for a particular stay or a statement of room availability.

Column Required Type and rules
base_price No Positive amount followed by an ISO 4217 currency code, such as 149.00 USD; up to 64 characters. Currency is normalized to uppercase.
star_rating No Number from 1.0 to 5.0 in increments of 0.5.
category No One scalar hotel category; up to 200 characters.
ads_metadata No JSON object with up to 50 string-valued entries. Nonempty keys can have up to 80 characters; nonempty values can have up to 200. Keys must be unique without regard to capitalization.

For example, an ads_metadata value before CSV escaping is {"custom_label_0":"waterfront"}. Keep metadata inside this JSON object; a top-level custom_label_0 column isn’t part of the Hotel schema. Confirm supported Hotel Set filters with your OpenAI account team; this reference defines the feed format, not campaign targeting.

Guest ratings

Guest ratings are optional. Supply up to 20 complete groups using zero-based guest_rating[n].* columns. Ratings are preserved in numeric index order. Every populated group must include all four fields; an entirely blank group is omitted.

Column Required within a populated group Type and rules
guest_rating[n].rating_system Yes Rating-system identifier; up to 100 characters.
guest_rating[n].score Yes Finite number from 0 to max_score, inclusive.
guest_rating[n].max_score Yes Finite number greater than 0 and no greater than 100.
guest_rating[n].number_of_reviewers Yes Whole-number count from 0 to 9,223,372,036,854,775,807 (2⁶³ − 1).

For one rating, include these four columns and values:

guest_rating[0].rating_system,guest_rating[0].score,guest_rating[0].max_score,guest_rating[0].number_of_reviewers
Example Reviews,8.8,10,425

This fragment isn’t a complete feed row; combine these columns with the required property columns. Use guest_rating[1].* for a second rating. Don’t put the ratings in a top-level guest_ratings JSON cell.

The legacy flattened alias guest_rating[n].number_of_raters is accepted for compatibility. Use number_of_reviewers in new files; conflicting alias values reject the row.

Derived and unsupported fields

OpenAI derives these values from the record and registered feed. Don’t supply them as overrides:

Field Derived value
item_id The property’s hotel_id.
inventory_type hotel.
schema_profile hotel_property_v1.
feed_id The registered feed receiving the upload.

Undocumented top-level columns aren’t ingested or used. Examples include room_id, checkin_date, length_of_stay, sale_price, tax, fees, phone, applink, priority, margin_level, custom_label_*, custom_number_*, and internal_label. Don’t add undocumented keys inside image, location, or guest-rating objects.

This beta submission contract covers full snapshots only. Partial or incremental updates, the Delta Feed API, XML, room or offer entities, dynamic rates, and stay-date availability are outside its scope.

Upload checklist

Before submitting a full snapshot:

  • Include every required property field and all four required location columns.
  • Encode images, location.neighborhoods, and ads_metadata as valid JSON cells.
  • Use complete guest_rating[n].* groups, not a guest_ratings JSON cell.
  • Replace sample URLs with publicly accessible property and image URLs.
  • Set is_ads_eligible explicitly for the properties you intend to advertise.
  • Treat base_price as a static starting price, not dated availability.

Check Upload History in Ads Manager after submitting the file. A completed file transfer doesn’t mean every row was accepted or that an ad can serve. Review row diagnostics and resolve invalid values before submitting the next full snapshot.