Skip to content

Platform

How TravelSpy works Traveller intelligence AI traveller profiles Data sources Journey orchestration Integrations Live demo

Solutions

Tour operators Airlines Online travel agencies Hotels & resorts Cruise lines Travel advisors Pricing

Resources

Insights library Case studies Documentation & API Trust & security

Company

About us Careers Contact Book a demo Log in

Home/ Resources/ Documentation

Developers

Documentation & API

Ship the tag in an afternoon, read a profile in one call, and push audiences wherever you need them.

Install the tag

Drop the loader into the head of every page. It is asynchronous, under 9 KB gzipped, and makes no network calls until consent is granted.

<script>
  (function (t, s) {
    t.TravelSpy = t.TravelSpy || function () { (t.TravelSpy.q = t.TravelSpy.q || []).push(arguments); };
    var e = s.createElement('script'); e.async = 1;
    e.src = 'https://cdn.travelspy.io/ts.js';
    s.head.appendChild(e);
  })(window, document);

  TravelSpy('init', { workspace: 'ws_live_xxxxxxxx', region: 'eu' });
</script>

Sending events

Call track for anything meaningful. Standard travel events are recognised by the model automatically; anything custom becomes a feature you can segment on.

TravelSpy('track', 'destination_viewed', {
  destination: 'JP',
  region: 'Kansai',
  trip_type: 'culture',
  nights: 11,
  party: { adults: 2, children: 0 }
});

TravelSpy('track', 'search_performed', {
  origin: 'MAN', destination: 'KIX',
  depart: '2026-04-11', return: '2026-04-22',
  cabin: 'economy', results: 38
});

Server-side ingestion is available on Navigate and Atlas via POST /v1/events with the same payload shape.

Profile API

Read the current modelled profile for a traveller. Median latency is under 200 ms at the edge.

GET https://api.travelspy.io/v1/profiles/{traveller_id}
Authorization: Bearer sk_live_xxxxxxxx

200 OK
{
  "traveller_id": "trv_8813af",
  "confidence": "high",
  "observed_signals": 214,
  "destination_affinity": [
    { "code": "JP", "score": 0.91 },
    { "code": "KR", "score": 0.44 }
  ],
  "trip_type": "culture_and_food",
  "party": { "adults": 2, "children": 0, "confidence": 0.86 },
  "spend_band": { "band": 6, "range_gbp": [6400, 8200], "price_sensitivity": "low" },
  "booking_window": { "opens_in_days": 9, "closes_in_days": 21 },
  "consent": { "analytics": true, "personalisation": true, "advertising": false }
}

Package API

Ask the orchestration engine for the assembled trip. Guardrails you configure in the portal are applied before the response is returned.

POST https://api.travelspy.io/v1/packages
{
  "traveller_id": "trv_8813af",
  "currency": "GBP",
  "components": ["flight", "accommodation", "transfer", "experience", "insurance"],
  "margin_floor_pct": 12
}

Webhooks

Subscribe to model changes rather than polling. Payloads are signed with an HMAC header you should always verify.

POST /your-endpoint
X-TravelSpy-Signature: t=1770000000,v1=8f1c...

{
  "event": "profile.booking_window_opened",
  "traveller_id": "trv_8813af",
  "occurred_at": "2026-03-02T09:14:22Z"
}

Available events include profile.created, profile.segment_changed, profile.booking_window_opened, package.assembled and consent.withdrawn.

Audience export

Segments can be pushed to ad platforms, your CRM or your warehouse. Consent state is enforced at the point of export, so an advertising-suppressed traveller is never included in an ad audience regardless of segment membership.

POST https://api.travelspy.io/v1/audiences/{audience_id}/sync
{ "destination": "meta_ads", "account_id": "act_123456789" }

TravelSpy will not store or score a traveller until a consent decision is recorded. Integrate with your CMP or set it directly.

TravelSpy('consent', {
  analytics: true,
  personalisation: true,
  advertising: false
});

Calling TravelSpy('forget') or DELETE /v1/profiles/{traveller_id} erases the profile and propagates the deletion to every connected destination.

Rate limits & errors

Default limits are 1,000 requests per minute for read endpoints and 200 per minute for write endpoints, raised on request. Errors use standard HTTP codes with a machine-readable body.

429 Too Many Requests
{ "error": "rate_limited", "retry_after_ms": 1400 }

Need a sandbox workspace?

Want a sandbox key?

We will set up a sandbox workspace with synthetic traveller data so your engineers can build against the real API before any commercial conversation.