Proxies for Ticketing and Event Registration
Proxies for ticketing and event registration: monitor public prices, detect scalping, and load test your own on-sale, inside the BOTS Act line.

Proxies for ticketing and event registration have exactly one legitimate job: letting you observe a public market accurately from many places at once, and letting you test infrastructure you own. They are not a way to buy tickets in bulk, and using them to do that is a federal offence in the United States. This guide is written for the pricing analyst, the rights-holder running anti-scalping enforcement, and the venue engineer who has to survive an on-sale. It covers where the legal boundary sits, how to build a collector that provably cannot cross it, and what the data actually looks like when you read it from six countries instead of one office IP.
Key Takeaways
- The US BOTS Act of 2016 (15 U.S.C. § 45c) makes it illegal to circumvent access controls or posted purchase limits on a ticket seller's site, and illegal to resell tickets obtained that way. The FTC's first enforcement actions, in January 2021, carried judgments totalling $31.6 million.
- Legitimate work is read-only: public price and availability monitoring, secondary-market analytics, scalping detection by rights-holders, and load testing or QA of systems you own.
- Ticket prices and inventory are geo-variable. Presales, tour-leg rights, currency, and fee display all change with the viewer's country, so a single-region view of a global on-sale is wrong data.
- Since the FTC fee rule took effect on 12 May 2025, US live-event sellers must show the all-in total up front, which makes fee disclosure a measurable, per-market compliance check.
- A monitoring bot should never enter a virtual waiting room. A queue slot consumed by an observer is a slot taken from a fan.
Where the Legal Line Sits
Start here, because everything else depends on it.
The Better Online Ticket Sales Act of 2016, codified at 15 U.S.C. § 45c and enforced by the FTC as an unfair or deceptive practice, prohibits two things. First, circumventing a security measure, access control system, or other technological control that a ticket seller uses to enforce posted purchase limits or to maintain the integrity of its posted order rules. Second, selling or offering to sell tickets you know were obtained that way. The FTC brought its first cases under the statute in January 2021 against three New York ticket brokers who used software to buy tens of thousands of tickets, with judgments totalling $31.6 million, most of it suspended on inability to pay.
Other jurisdictions have parallel rules. Section 106 of the UK Digital Economy Act 2017 created a criminal offence of using automated software to buy more tickets than a seller permits. Ontario's Ticket Sales Act, 2017 bans both bot purchasing and reselling bot-obtained tickets. The direction of travel is the same everywhere.
Read that as a bright line rather than a grey zone:
| Off-limits | Legitimate |
|---|---|
| Automating purchases or exceeding a per-order or per-account limit | Reading publicly displayed prices and availability |
| Creating or cycling accounts to gain more buying attempts | Analysing public secondary-market listings |
| Entering, holding, or multiplying positions in a virtual waiting room | Observing from outside the queue, after inventory publishes |
| Defeating a CAPTCHA or access control that gates purchase | Collecting pages open to any logged-out visitor |
| Reselling tickets acquired through any of the above | Rights-holder enforcement and evidence collection |
| Stress-testing somebody else's ticketing platform | Load testing a system you own, with written authorization |
The technical capabilities are similar in both columns. Intent, target, and whether you ever touch a transactional path are what separate them. This guide stays in the right-hand column, and the collector design below enforces that in code rather than leaving it to good intentions. The retail-automation side of this problem space is a different topic with a different risk profile, covered in datacenter proxies for sneaker botting and retail automation.
Who Actually Needs This
Four groups run this workload in production.
Pricing and market analysts. Promoters, teams, festival operators, and travel companies that package tickets need to know what comparable events cost now, how secondary prices move between announcement and doors, and how much of the displayed total is face value versus fees. That is a public-data problem across dozens of sites and several currencies.
Rights-holders and anti-scalping teams. Artists, leagues, and venues want to know which secondary listings correspond to inventory that has not gone on sale yet, which brokers list blocks exceeding posted limits, and whether restricted inventory is being advertised anyway. The output is evidence, not tickets.
Venue and platform engineers. If you run the registration system, the question is how it behaves when 200,000 people arrive in 90 seconds from 40 countries through different CDN edges. Testing that from one datacenter region gives a comfortable, misleading answer.
Marketing and localisation QA. Event pages, currency display, fee breakdowns, and presale banners vary by region, so checking them means viewing your own pages as a buyer in each market sees them, the same problem covered in proxies for localization testing.
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
What the Same Event Looks Like From Six Countries
Ticketing is one of the more strongly geo-differentiated verticals on the web, and the variation is not cosmetic. Five separate mechanisms change what the page says.
Rights and availability by territory. Tour legs, broadcast rights, and local promoter deals mean an event may not be listed at all outside its territory, or may route to a national storefront holding different inventory.
Presale gating. Card-issuer, fan-club, and mobile-carrier presales are frequently country-scoped. From the wrong country you see "no tickets available" during a window when a whole allocation is live somewhere else.
Currency and tax display. The same seat renders in USD with US tax rules, in GBP with VAT included, and in EUR with a local service charge, and the arithmetic between those numbers is not a simple FX conversion.
Fee disclosure rules. The FTC Rule on Unfair or Deceptive Fees (16 CFR Part 464) took effect on 12 May 2025 and requires live-event ticket sellers to display the total price, inclusive of mandatory fees, up front. UK and EU rules differ again, so the headline number on one seat is constructed differently by market.
Queue and bot-mitigation posture. Sites tighten regional rules around an on-sale, and traffic from an unexpected region is challenged sooner.
| Signal | Why it varies by IP | What to capture |
|---|---|---|
| Listing existence | Territorial rights, geo-routing to a national storefront | Event ID, storefront domain, HTTP status |
| Displayed total | Fee-inclusive rules differ by jurisdiction | Face value, itemised fees, all-in total, currency |
| Availability state | Presale allocations are country-scoped | Section, quantity, on-sale state, timestamp |
| Delivery and transfer terms | Local consumer law, transfer restrictions | Delivery method, transfer-allowed flag |
| Queue behaviour | Regional traffic shaping before an on-sale | Whether a queue interstitial was served |
The consequence: pin one country per stream and hold it for the whole series. If your pool silently rotates a stream from a GB exit to a US exit mid-run, your "price movement" chart is really a chart of your own proxy rotation. Background in what geo-targeting means in proxies.
Choosing the Right Proxy Type
| Proxy type | Fits | Weakness | Typical use here |
|---|---|---|---|
| Datacenter | Public event indexes, sitemaps, low-defence secondary sites, bulk sweeps | Datacenter ASNs are pre-scored as suspicious by ticketing bot mitigation | Wide, cheap discovery crawls |
| Residential | Region-locked storefronts, presale-gated pages, primary sellers behind commercial bot management | Costs more per request, slower | Accurate per-country price capture |
| Mobile | The most heavily defended app-first sellers | Most expensive, smallest pools | Spot checks, not bulk collection |
| Your own egress | Load testing systems you own | No third-party realism | Baseline before distributed tests |
The realistic mix is a datacenter pool for discovery plus a residential pool with per-country targeting for anything price-bearing. Primary ticketing platforms run the same commercial bot-mitigation stacks as banks and airlines, and they tune them hardest in the hours around an on-sale. A collector that worked fine on Tuesday can start collecting challenge pages on Friday at 09:59 local. Plan for it, and treat a sudden run of interstitials as a signal to back off rather than to push harder.
Building a Collector That Cannot Buy Anything
This is the part most articles skip, and it is the part that matters legally. Do not rely on your crawler "not being configured" to reach checkout. Make the transactional paths unreachable by construction, and log every rejection.
Three guarantees are worth enforcing in code:
- Method allow-list. Only GET and HEAD leave the collector. A ticket purchase, a cart hold, and an account creation are all POSTs or PUTs. If your collector cannot issue one, it cannot buy.
- Path deny-list. Any URL whose path touches cart, checkout, queue, waiting room, reserve, hold, or account is refused before a request is built.
- Host allow-list. Only the domains on your approved monitoring list. This stops a stray link in a parsed page from walking the crawler somewhere it should not go.
from urllib.parse import urlparse
BLOCKED_PATH_TOKENS = (
"/cart", "/checkout", "/basket", "/queue", "/waitingroom",
"/reserve", "/hold", "/signin", "/login", "/register", "/account",
)
ALLOWED_HOSTS = {"tickets.sparkproxy.io", "events.sparkproxy.io"}
class ObservationBoundary(Exception):
pass
def assert_observation_only(url: str, method: str = "GET") -> str:
"""Refuse anything that could transact. Raises before a request is built."""
if method.upper() not in ("GET", "HEAD"):
raise ObservationBoundary(f"write method blocked: {method}")
parts = urlparse(url)
if parts.hostname not in ALLOWED_HOSTS:
raise ObservationBoundary(f"host not on monitoring allow-list: {parts.hostname}")
path = parts.path.lower()
for token in BLOCKED_PATH_TOKENS:
if token in path:
raise ObservationBoundary(f"transactional path blocked: {token}")
return url
Two rules sit alongside the code. Never send credentials, because an authenticated session is a participant session. And never accept a queue token: if a response sets one, drop it rather than persisting it, so the collector cannot accumulate queue position across runs. Waiting rooms allocate scarce, ordered slots, and an observer holding one has taken it from a buyer. How Queue-it virtual waiting rooms work explains the mechanism you are declining to enter.
Log every ObservationBoundary exception with the URL and the reason. That log is what you hand to counsel when somebody asks what your system was doing during an on-sale.
Timing Collection Around an On-Sale
An on-sale is a stress event for the seller and a data event for you, and those two facts conflict. The discipline that separates a responsible monitor from an incidental load generator is backing off exactly when the site is busiest, because you are not competing for inventory and have no reason to be there at T+0.
| Window | Goal | Suggested cadence | Notes |
|---|---|---|---|
| Announcement to T-24h | Baseline: sections, face values, on-sale times, presale structure | Every 6 hours | Cheap datacenter sweeps are fine here |
| T-24h to T-1h | Capture presale states per country | Hourly, one stream per country | Residential, pinned `country_code` |
| T-1h to T+15m | Do not add load | Suspended, or one heartbeat every 10 minutes | The peak belongs to buyers |
| T+15m to T+6h | Sell-through curve and first secondary listings | Every 5 to 10 minutes | This is where the interesting data is |
| T+6h to event | Price decay, inventory releases, broker behaviour | Hourly, then daily | Watch for held-back inventory drops |
Two details make the series usable. Timestamp in UTC at the moment of capture, not from the page's rendering of local time. And store append-only, keyed by (event, storefront, country, section, captured_at), because every insight here lives in the diff between two rows. Overwriting destroys the movement you were collecting in the first place. The pacing philosophy is the same one in ethical scraping and rate limiting.
Collecting Public Ticket Data With the SparkProxy Scraping API
The SparkProxy Scraping API folds proxy rotation, country targeting, and headless rendering into one request, so the collector stays small. The base URL is https://scrape.sparkproxy.io/api/v1 and auth is the X-API-Key header. Every target below uses a sparkproxy.io demo path; substitute the public listing page you are authorized to monitor, and route it through the boundary check first.
A single geo-targeted read of a public event page, rendered and returned as structured JSON:
curl -X GET "https://scrape.sparkproxy.io/api/v1?url=https://events.sparkproxy.io/demo/event/9184&country_code=GB&premium_proxy=true&render_js=true&format=json" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx"
The two parameters carrying the use case are country_code, which decides which storefront and fee presentation you see, and premium_proxy, which routes through the residential tier so the request clears ASN checks that datacenter ranges fail.
Reading the same event from several markets in one pass, with the boundary check in front of every call:
import datetime as dt
import json
import requests
API = "https://scrape.sparkproxy.io/api/v1"
KEY = "sk-xxxxxxxxxxxxxxxx"
MARKETS = ["US", "GB", "DE", "CA", "AU", "JP"]
EVENT_URL = "https://events.sparkproxy.io/demo/event/9184"
def read_market(url, country):
assert_observation_only(url) # GET only, no cart, no queue
resp = requests.get(API, headers={"X-API-Key": KEY}, params={
"url": url,
"country_code": country, # pin the market for the whole series
"premium_proxy": "true", # residential exit
"render_js": "true",
"format": "json",
"tag": f"ticket-monitor-{country}",
"session_id": f"tix-{country}",
}, timeout=90)
resp.raise_for_status()
return {
"country": country,
"captured_at": dt.datetime.now(dt.timezone.utc).isoformat(),
"job_id": resp.headers.get("X-Job-Id"),
"credits": resp.headers.get("X-Credits-Used"),
"payload": resp.json(),
}
with open("event_9184.ndjson", "a", encoding="utf-8") as fh:
for market in MARKETS:
fh.write(json.dumps(read_market(EVENT_URL, market)) + "\n")
Pulling structured fields instead of raw HTML, using extract_rules on a public listing board:
params = {
"url": "https://events.sparkproxy.io/demo/event/9184/listings",
"country_code": "US",
"premium_proxy": "true",
"render_js": "true",
"wait_for": ".listing-row",
"extract_rules": json.dumps({
"face_value": ".price-summary .face",
"service_fee": ".price-summary .service-fee",
"facility_fee": ".price-summary .facility-fee",
"all_in_total": ".price-summary .total",
"currency": ".price-summary .currency",
"section": ".listing-row .section",
"quantity": ".listing-row .qty",
"seller_type": ".listing-row .seller-badge",
}),
"format": "json",
}
rows = requests.get(API, headers={"X-API-Key": KEY}, params=params, timeout=90).json()
Capture face value and each fee component separately, not just the total. The gap between them is the whole analysis, and after the FTC fee rule it is also the compliance signal.
For a wide, cheap sweep across many event index pages, batch mode takes comma-separated URLs and requires plain HTTP:
curl -X GET "https://scrape.sparkproxy.io/api/v1?render_js=false&url=https://events.sparkproxy.io/demo/venue/101,https://events.sparkproxy.io/demo/venue/102,https://events.sparkproxy.io/demo/venue/103" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx"
The response is a JSON object with a results array, one entry per URL, each carrying url, success, httpStatus, and body. Use it for discovery, then re-read anything price-bearing with premium_proxy and rendering on.
For long sweeps, hand off to async delivery with callback_url and let the webhook write to your store:
curl -X POST "https://scrape.sparkproxy.io/api/v1" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://events.sparkproxy.io/demo/event/9184/listings",
"country_code": "DE",
"premium_proxy": true,
"render_js": true,
"format": "json",
"callback_url": "https://hooks.sparkproxy.io/ingest/ticket-monitor"
}'
That returns 202 Accepted with a job_id, and the full result arrives at your endpoint when it completes. Results are also retrievable from /api/v1/files/:jobId using the X-Job-Id value returned in the response headers.
Detecting Scalping and Speculative Listings
For a rights-holder, the valuable output is not a price. It is a defensible claim that a specific listing should not exist. Three detections do most of the work, and all three come from cross-referencing public secondary listings against your own primary inventory manifest.
Speculative listings. A "spec" listing offers tickets the seller does not hold, usually posted before the public on-sale in the hope of sourcing them later. Any secondary listing first seen before your on-sale time is not backed by inventory sold through your primary channel.
Block sizes above your posted limit. If your limit is six per household and a professional seller lists a contiguous block of ten in one section, that is worth an entitlement review.
Sections that do not exist. Listings referencing a section or row absent from your manifest indicate either fabrication or a mapping problem worth chasing.
def flag_listings(listings, onsale_utc, primary_sections, per_order_limit=6):
flags = []
for row in listings:
if row["first_seen_utc"] < onsale_utc:
flags.append((row["listing_id"], "listed before public on-sale"))
if row["section"] not in primary_sections:
flags.append((row["listing_id"], "section absent from primary manifest"))
if row["quantity"] > per_order_limit and row["seller_type"] == "professional":
flags.append((row["listing_id"], f"block of {row['quantity']} exceeds posted limit"))
return flags
A flag is not evidence. Evidence is a timestamped, reproducible capture of the page as it appeared to a buyer in a given market, which is what format=screenshot and format=pdf are for:
curl -X GET "https://scrape.sparkproxy.io/api/v1?url=https://events.sparkproxy.io/demo/listing/558231&country_code=US&premium_proxy=true&render_js=true&format=pdf" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx" \
--output listing-558231-2026-08-18T14-02Z.pdf
Name the file with the UTC capture time and the market, store the X-Job-Id alongside it, and keep the raw JSON capture. A PDF plus a JSON record plus a job ID is a chain of custody a takedown notice or a regulator can follow, the same evidence discipline described in using proxies for brand protection online.
Load Testing Your Own Registration System
If you operate the registration platform, the on-sale is your worst hour of the year, and single-region load tests hide the failures that actually happen. Distributed egress changes four things a local test cannot reproduce. CDN edge selection routes each region to a different POP with its own cache state. Per-IP rate limits and WAF rules behave completely differently when 10,000 requests arrive from 10,000 addresses instead of 50. Geo-DNS may send different regions to different origin clusters. And real-world latency and packet loss expose timeouts a low-latency test never triggers.
The rules are strict, and they are not optional:
- Test only systems you own or have written authorization to test. Sending synthetic load at a third-party ticketing platform is a denial-of-service attack under the US Computer Fraud and Abuse Act and the UK Computer Misuse Act 1990, whatever you call it internally.
- Notify your hosting provider, CDN, and bot-mitigation vendor and get sign-off. Most run a formal simulated-event process and will otherwise treat your test as an attack.
- Use a maintenance window against staging that mirrors production topology, or production only with explicit change approval.
- Build a kill switch that stops every generator within seconds, with a named person on the call who can pull it.
- Watch queue admission rate, origin error rate, database connection saturation, and CDN cache hit ratio, not just aggregate requests per second. Registration systems rarely fail on bandwidth. They fail on lock contention, session-store capacity, or the queue's admission logic.
Ramp in stages: a smoke pass at 1% of expected peak, then 25%, 50%, 100%, and finally 150% to find the break point. Record which layer failed first at each step. That list, not the peak number, is the deliverable.
Geo QA and Fee-Disclosure Checks on Your Own Pages
Your own event pages render differently by market for the same reasons everyone else's do, and the failures are quiet ones: a presale banner that never shows in Canada, a currency selector that defaults wrong, a fee breakdown that renders correctly in the US template but drops the mandatory-fee line in the EU one.
Under the FTC fee rule, US live-event sellers have had to show the fee-inclusive total up front since 12 May 2025. That turns a design question into a testable assertion: fetch your own public listing page from a US exit, extract the headline number and the itemised components, and assert that the headline equals face value plus every mandatory fee.
def assert_all_in(fields):
"""Headline price must already include every mandatory fee."""
face = money(fields["face_value"])
fees = sum(money(fields[k]) for k in ("service_fee", "facility_fee") if fields.get(k))
headline = money(fields["all_in_total"])
if abs(headline - (face + fees)) > 0.01:
raise AssertionError(
f"headline {headline} excludes mandatory fees (face {face} + fees {fees})"
)
Run that assertion per market on every deploy, alongside a rendered screenshot for the visual record. Where the page needs interaction before the price panel resolves, drive it with js_scenario on your own domain rather than clicking through by hand in six locales:
{
"url": "https://events.sparkproxy.io/demo/event/9184",
"country_code": "DE",
"premium_proxy": true,
"render_js": true,
"wait_for": ".price-summary",
"js_scenario": {
"instructions": [
{"click": "#accept-cookies"},
{"click": "[data-currency='EUR']"},
{"wait": 1200}
]
},
"format": "json"
}
Note what this scenario does not do: it selects a currency and reads a panel. It never adds to a basket, never enters a queue, and never authenticates, and that holds even on your own property, so the same collector code stays safe if somebody later points it at a domain you do not own.
Where to Start
- Write down which sites you will monitor and on what basis, then encode that list as the host allow-list. If a domain is not on it, the request never gets built.
- Ship the boundary check before any parsing code, and log its rejections. It takes an afternoon.
- Pin one country per stream with
country_code, run residential for anything price-bearing, and hold the pinning for the life of the series. - Capture face value, each fee, and the all-in total as separate fields, in UTC, append-only.
- Suspend collection through the on-sale peak. Resume 15 minutes after, when the sell-through curve is the interesting signal anyway.
- For load testing, get written authorization and vendor sign-off before generating a single synthetic request.
The hard part of this workload was never the scraping. It is being able to demonstrate, from your code and your logs, that you were observing a market rather than participating in it.
Frequently asked questions
FAQ
Reading publicly displayed prices and availability is ordinary public-data collection and is generally lawful, subject to each site's terms of service and local law. What is illegal in the US under the BOTS Act is circumventing purchase limits or access controls, and reselling tickets obtained that way. Proxies used purely to observe a public market from multiple regions sit on the legal side of that line.
No. 15 U.S.C. § 45c targets circumventing security measures and access control systems that enforce posted purchase limits or order rules, plus the resale of tickets obtained through such circumvention. It is not a general prohibition on reading public pages. The safe design is a collector that cannot transact: GET and HEAD only, no credentials, no cart or queue paths.
Residential proxies with per-country targeting for anything price-bearing, because primary ticketing platforms run commercial bot mitigation that scores datacenter ASNs as suspicious before your request is even parsed. Keep a datacenter pool for cheap discovery sweeps of event indexes, and reserve mobile IPs for spot checks on the most heavily defended sellers.
Yes, and distributed egress is the only way to get a realistic result, because CDN edge routing, per-IP rate limits, and geo-DNS all behave differently under many source addresses. Test only systems you own or have written authorization to test, notify your hosting, CDN, and bot-mitigation vendors in advance, and keep a kill switch with a named operator on the call.
Territorial rights, country-scoped presales, local currency and tax display, and jurisdiction-specific fee-disclosure rules all change what the page shows. Since the FTC fee rule took effect on 12 May 2025, US live-event sellers must display the fee-inclusive total up front, so the same seat can present a very different headline number in two markets.
No. A waiting room allocates ordered, scarce positions, so an observer holding one has taken it from a real buyer, and passing through an access control that gates purchase is exactly what the BOTS Act prohibits. Collect from outside the queue: public event indexes, secondary listings, and post-on-sale availability give you the data without consuming a slot.
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles

Proxies for Web3 Data and NFT Marketplace Feeds
Proxies for Web3 data: where they fix IPFS gateway and marketplace throttling, where an API key makes them useless, and how to collect NFT floor and trait data.

Proxies for Streaming Catalog Research
How proxies for streaming catalog research track which titles are listed in which country, licensing window churn and regional price tiers. Metadata only.

Antidetect Browsers for Market Research
Using antidetect browsers for market research: competitor pricing, ad verification, localized SERP and review checks, and the geo coherence they require.
