This page walks through the canonical pattern for importing many events programmatically — for example, a venue operator publishing a season schedule, a festival uploading three hundred sessions, or a Shopify merchant migrating from another ticketing platform.
Why use the API for bulk creation
The Guest Manager admin UI is fine for ad-hoc event creation. But when you have:
- A spreadsheet of 100+ events to publish
- A schedule that’s generated by an external system (calendar, content database, partner feed)
- A migration from another ticketing platform
- An ongoing content pipeline that pushes new events as they’re confirmed
…API-driven creation is the only sane path. This guide assumes you’re writing a script (any language) that authenticates with a company API key and walks through the dependency graph below.
Dependency order
Events depend on other resources. Create them in this order:
1. Venues
↓
2. Ticket fields (custom attendee questions: dietary, t-shirt size, etc.)
↓
3. Ticket types (reusable ticket configuration; references ticket fields)
↓
4. Events (references venue + registration types that reference ticket types)
You only have to create venues / ticket fields / ticket types once each — they’re reusable across all events. So a “bulk import” script typically:
- Looks up or creates the small set of shared resources (venues, ticket fields, ticket types) — usually a few rows
- Iterates through the events to create, calling the events endpoint per row
Step 1: Venues
Look up existing venues first; create only the ones missing.
# Find an existing venue by name
curl -X GET \
'https://app.guestmanager.com/api/public/v2/venues?filter[search]=Texas Motor Speedway' \
-H 'Authorization: Token abcdefg'
# Create one if it doesn't exist
curl -X POST \
https://app.guestmanager.com/api/public/v2/venues \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"venue": {
"name": "Texas Motor Speedway",
"time_zone": "Central Time (US & Canada)",
"address": {
"address1": "3545 Lone Star Cir",
"city": "Fort Worth",
"zipcode": "76177",
"country_code": "US",
"state_code": "TX"
}
}
}'
Cache the returned id — every event uses it.
See Venues for the full reference.
Step 2: Ticket fields
Create any custom fields your tickets need to capture. Skip if your events don’t need attendee questions.
curl -X POST \
https://app.guestmanager.com/api/public/v2/ticket_fields \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"ticket_field": {
"name": "T-shirt size",
"kind": "select",
"required": true,
"options": [
{ "name": "Small", "position": 1 },
{ "name": "Medium", "position": 2 },
{ "name": "Large", "position": 3 }
]
}
}'
Cache the returned id for the next step.
See Ticket fields.
Step 3: Ticket types
Define the reusable ticket configurations — typically “GA”, “VIP”, “Press”. Each ticket type can attach the ticket fields from step 2.
curl -X POST \
https://app.guestmanager.com/api/public/v2/ticket_types \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"ticket_type": {
"name": "GA",
"transferable": true,
"include_name": true,
"include_email": true,
"fields": [42, 43]
}
}'
Cache the returned id. The next step references it from every event.
See Ticket types.
Step 4: Events
For each row in your import:
curl -X POST \
https://app.guestmanager.com/api/public/v2/events \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"event": {
"name": "Friday Night Race",
"venue_id": 2847,
"starts_at": "2026-09-12T19:00:00Z",
"ends_at": "2026-09-12T23:00:00Z",
"published_at": "2026-05-08T00:00:00Z",
"capacity": 5000,
"registration_types": [
{ "name": "GA", "ticket_type_id": 7456, "price": 50, "inventory": 4500 },
{ "name": "VIP", "ticket_type_id": 7456, "price": 125, "inventory": 500 }
]
}
}'
The response includes the new event’s id. Capture the X-Event-Sync-Job-ID response header for the next step.
See Events.
Step 5: Wait for sync
Each event creation kicks off async work (recurrence fan-out, ticket structure setup, Shopify product mirroring for Shopify-installed companies). For a bulk import, you have two strategies:
Fire-and-forget
If your script doesn’t need to do anything immediately after each event, just keep importing. Sync work parallelizes across events.
Wait per event
If subsequent steps in your script depend on the event being fully synced (e.g., you want to immediately import comp tickets to it), poll the sync job:
curl -X GET \
https://app.guestmanager.com/api/public/v2/events/71390/sync_jobs/4291 \
-H 'Authorization: Token abcdefg'
Use exponential backoff (1s → 2s → 4s → 8s, capped at 15s). Stop polling when status is completed or failed.
See Sync jobs.
Practical considerations
Rate limiting
The default rate limit is 300 requests per minute per IP. For an import of 1000 events, that’s a hard floor of about 3.5 minutes. If you’ll need higher throughput, contact support before kicking off the import — we can raise the limit or coordinate a maintenance window.
The Retry-After response header tells you when to resume after a 429.
Idempotency
The API does not currently support idempotency keys. If your bulk import script fails partway through and you re-run it, you’ll get duplicate events.
Mitigation patterns:
- Track creation locally: write each created event’s ID back to your source data before moving to the next row. On retry, skip rows that already have an ID.
- De-duplicate by name + date: query existing events with
filter[name]=...&filter[starts_at][from]=...before each create.
Concurrency
Run your import single-threaded. Concurrent updates to the same event return 409 Conflict (sync job in progress). Concurrent creates of different events are safe but quickly hit the rate limit.
Error handling
Common failure modes:
| Status | Type | Cause |
|---|---|---|
400 |
invalid_parameters |
Missing or wrong-typed parameter. Check the response detail.params. |
404 |
not_found |
A referenced ID (venue, ticket type, ticket field) doesn’t exist or was deleted. |
409 |
uneditable |
Sync job in progress. Wait, then retry. |
422 |
validation_error |
Validation failed. The response errors block tells you which field. |
429 |
(no body) | Rate limited. Wait Retry-After seconds. |
See Errors for the full reference.
Updates and re-runs
Events accept PATCH updates with the same parameter shape — pass only the fields that changed. To update registration types in place, include them in the request with their existing ids; omitting an existing registration type does not delete it. To remove one, pass { "id": <id>, "_destroy": true }.
Rollout pattern
For a first import, we recommend:
- Dry run with a single event. Verify the event appears in the admin UI as expected, with the right venue, ticket types, and registration types.
- Small batch of 5-10 events. Confirm the pattern handles your edge cases.
- Full batch with the patterns above (single-threaded, idempotency tracking, error handling).
For migrations from another ticketing platform — particularly when you need to preserve existing barcodes — contact support. We can help script the import and have a few migration tools that aren’t part of the public API.