Skip to main content
No live events right now

Fan SDK

Drop in. Fan graph included.

The official TypeScript/JavaScript SDK for revolution.fan. Works in browser, SSR, React Native, and Node.js. One import gives you verified fan identity, $FAN balance, attendance history, and real-time event context.

TypeScriptReact NativeNode.jsREST

Installation

bash
npm install @revolution/sdk
# or
pnpm add @revolution/sdk
# or
yarn add @revolution/sdk

Initialisation

typescript
import { Revolution } from '@revolution/sdk';

const rev = new Revolution({
  apiKey: 'rev_live_xxxx',       // required — get yours at revolution.fan/developers
  baseUrl: 'https://api.revolution.fan/api/v1', // default shown
  defaultRegion: 'atx',          // optional — pre-fills region on all queries
  timeout: 10_000,               // optional — ms (default 10 000)
  authScheme: 'bearer',          // optional — see Auth docs
});

Events

Discover, search, and retrieve live events. Available on all key tiers.

List events

typescript
const { items, total, hasMore } = await rev.events.list({
  region: 'atx',           // required — region slug
  dateFrom: '2026-05-01',  // ISO date
  dateTo: '2026-05-31',
  genre: 'hip-hop',        // optional filter
  featured: true,          // only featured events
  page: 1,
  pageSize: 20,
});

Get a single event

typescript
const event = await rev.events.get('khruangbin-stubbs-2026-05-14');

console.log(event.title);       // "Khruangbin at Stubb's"
console.log(event.date);        // "2026-05-14T21:00:00-05:00"
console.log(event.venue.name);  // "Stubb's Outdoor Amphitheater"
console.log(event.ticketUrl);   // "https://..."
console.log(event.imageUrl);    // hero image

Today & tomorrow feed

typescript
const region = await rev.regions.nearest(lat, lng);
const { today, tomorrow } = await rev.events.todayTomorrow(region.slug);

Create an open event

Any authenticated user can create a public event in seconds — no venue account required. Ideal for conference side events, community meetups, birthday parties, and anything in between. Every attendee still earns $FAN and receives an on-chain proof of attendance on check-in.

typescript
// POST /api/v1/events/quick  — requires a valid user Bearer token
const { event } = await rev.events.create({
  title: 'DeFi Happy Hour @ Consensus',  // required
  startTime: '2026-05-23T18:00:00-05:00', // required (ISO 8601)
  endTime: '2026-05-23T21:00:00-05:00',  // optional
  locationText: 'The Rusty Nail, Austin TX', // free-text — or "Online"
  description: 'Side event for Consensus attendees. Drinks on us.',
  isFree: true,                           // default true
  ticketPrice: undefined,                 // number (USD) when isFree=false
  imageUrl: 'https://...',               // optional hero image
});

console.log(event.slug);       // auto-generated from title + date
console.log(event.eventType);  // "open_event"
// redirect user to: /events/{event.slug}

Venues & Artists

typescript
// Venue profile + upcoming shows
const venue = await rev.venues.get('stubbs-austin');
const shows = await rev.venues.upcomingShows('stubbs-austin', { limit: 10 });

// Artist profile + tour dates
const artist = await rev.artists.get('khruangbin');
const tour   = await rev.artists.tourDates('khruangbin', { region: 'atx' });

Commerce

Purchase tickets and auto-mint proof-of-attendance NFTs. Requires Pro or Enterprise key.

typescript
// Purchase tickets + optional NFT proof-of-attendance mint
const order = await rev.commerce.purchase({
  eventId: 1042,
  quantity: 2,
  paymentMethodId: 'pm_stripe_xxx',  // Stripe payment method ID
  buyerEmail: 'fan@example.com',
  walletAddress: '0xabc...',          // optional — triggers on-chain mint (SKALE, zero gas)
});

console.log(order.status);           // 'confirmed'
console.log(order.nftTokenId);       // '12345' — minted if walletAddress provided
console.log(order.tickets[0].qrCode); // scan at door
console.log(order.receiptUrl);       // PDF receipt

Loyalty & $FAN balance

typescript
const loyalty = await rev.commerce.loyalty('0xabc...');

console.log(loyalty.tier);       // 'superfan'
console.log(loyalty.fanPoints);  // 4200  — $FAN balance (off-chain, Phase 1)
console.log(loyalty.nfts);       // [ { tokenId, eventTitle, date, imageUrl } ]

Marketplace

Query verified fan profiles for label analytics, tour routing, and ad targeting. Requires a Marketplace key (mkt_xxxx) with authScheme: 'marketplace'.

typescript
const rev = new Revolution({ apiKey: 'mkt_xxxx', authScheme: 'marketplace' });

// Browse available attributes
const { catalog } = await rev.marketplace.getCatalog({ sortBy: 'available_users' });

// Preview — free, returns match count + cost estimate
const preview = await rev.marketplace.previewQuery({
  filters: [
    { key: 'top_genres',             operator: 'contains', value: 'hip-hop' },
    { key: 'concerts_attended_12m',  operator: 'gte',      value: '3' },
    { key: 'city',                   operator: 'eq',       value: 'Los Angeles' },
  ],
  attributes: ['top_genres', 'events_attended_rfan', 'average_ticket_spend'],
  queryType: 'aggregated',
  regulatoryMax: 'pii_low',
});

console.log(preview.matchedUsers);       // 4 200
console.log(preview.estimatedCostUsd);  // 8.40

// Execute — charges your account, distributes $FAN to matching fans
const result = await rev.marketplace.executeQuery({
  queryId: preview.queryId,
  deliveryFormat: 'json',
});

Attendance

Venue check-in and QR scanning. Requires a Scanner key.

typescript
const rev = new Revolution({ apiKey: 'scan_xxxx', authScheme: 'scanner' });

// Verify a fan's QR ticket
const result = await rev.attendance.verify({
  qrCode: '0xdeadbeef...',
  eventId: 1042,
  gate: 'MAIN',
});

console.log(result.valid);      // true
console.log(result.fanId);      // 'fan_0xc3f…'
console.log(result.fanName);    // 'Jordan' (if fan opted in to name sharing)
console.log(result.nftMinted);  // true — proof-of-attendance written on-chain

Embed Widget

Drop a self-contained event listing into any HTML page — no framework required.

html
<!-- Add to any website -->
<div id="events"></div>
<script src="https://cdn.revolution.fan/sdk/embed.js"></script>
<script>
  RevolutionEmbed.mount('#events', {
    apiKey: 'rev_live_xxxx',
    region: 'atx',
    theme: 'dark',
    maxEvents: 6,
    onTicketClick: (event) => {
      // Handle ticket purchase in your own UI
    },
  });
</script>

Error Handling

typescript
import { RevolutionError } from '@revolution/sdk';

try {
  const event = await rev.events.get('non-existent-slug');
} catch (err) {
  if (err instanceof RevolutionError) {
    console.log(err.status);   // 404
    console.log(err.code);     // 'EVENT_NOT_FOUND'
    console.log(err.message);  // human-readable
  }
}

Python SDK

The official Python client for revolution.fan. Designed for data science workflows, analytics pipelines, and backend services. Works with Pandas, Jupyter, and any Python 3.8+ environment.

Python 3.8+JupyterPandasAsync

Installation

bash
pip install revolution-fan

Initialisation

python
from revolution_fan import RevolutionFanClient

client = RevolutionFanClient(
    api_key="rev_live_xxxx",        # or set REVOLUTION_FAN_API_KEY env var
    base_url="https://api.revolution.fan/api/v1",  # default
    timeout=30,
)

Events

python
# List events
result = client.events.list(region="atx", featured=True, page_size=20)
for event in result["items"]:
    print(event["title"], event["date"])

# Get a single event
event = client.events.get("khruangbin-stubbs-2026-05-14")
print(event["venue"]["name"])

Data Marketplace

python
from revolution_fan import RevolutionFanClient

client = RevolutionFanClient(
    api_key="mkt_xxxx",
    auth_scheme="marketplace",
)

# Preview a query
preview = client.marketplace.preview_query(
    filters=[
        {"key": "top_genres",            "operator": "contains", "value": "hip-hop"},
        {"key": "concerts_attended_12m", "operator": "gte",      "value": "3"},
        {"key": "city",                  "operator": "eq",       "value": "Los Angeles"},
    ],
    attributes=["top_genres", "events_attended_rfan", "average_ticket_spend"],
    query_type="aggregated",
    regulatory_max="pii_low",
)

print(preview["matched_users"])        # 4200
print(preview["estimated_cost_usd"])   # 8.40

# Execute
result = client.marketplace.execute_query(
    query_id=preview["query_id"],
    delivery_format="json",
)
print(result["record_count"])          # 4200
print(result["download_url"])          # signed S3 URL

Sandbox Mode

python
# Use sandbox mode to test without a real API key
from revolution_fan import RevolutionFanClient

client = RevolutionFanClient(sandbox=True)  # no api_key required

result = client.marketplace.preview_query(
    filters=[{"key": "top_genres", "operator": "contains", "value": "hip-hop"}],
    attributes=["top_genres", "spend_band"],
    query_type="aggregated",
)
# Returns realistic synthetic data — safe to use in notebooks and CI

Jupyter Notebooks

Four sample notebooks are included in the repo under notebooks/ covering genre affinity segmentation, concert attendance lookalike modelling, cross-vertical fan profiles, and esports sponsor targeting.

bash
# Clone and run locally
git clone https://github.com/tokenevents/revolution-fan
cd revolution-fan/notebooks
jupyter notebook

Error Handling (Python)

python
from revolution_fan.exceptions import RevolutionFanError, AuthenticationError, RateLimitError

try:
    result = client.marketplace.execute_query(query_id="q_xxx")
except AuthenticationError:
    print("Invalid or expired API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except RevolutionFanError as e:
    print(f"API error {e.status_code}: {e.message}")