API / Developers
Hilo Public API (v1)
Create and read Hilo prediction markets programmatically with the v1 REST API. Markets you create here go live immediately and follow the same rules, rewards and side effects as markets created in the web app.
Status: beta. The endpoints below are live. The topic vocabulary is still being curated, so a name you expect may return
TOPIC_NOT_FOUND— fetchGET /api/v1/topicsfor the accepted set.
Quick start
curl -X POST https://hilo.com/api/v1/markets \
-H "Authorization: Bearer $HILO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Will the Fed cut rates before July 2027?",
"description": "Resolves YES if the FOMC announces a cut to the target range at or before the July 2027 meeting.",
"options": ["Yes", "No"],
"marketType": "event",
"topics": ["Economy", "Finance"],
"filterCountries": ["United States"],
"estimatedEndDate": "2027-07-31T00:00:00.000Z"
}'Authentication
Every request carries a key as a bearer token:
Create keys in Settings → API Keys. Premium and Premium+ only. The plaintext key is shown once at creation and never again — only a hash is stored. Lose it and you create a new one.
Keep your key on a server. Never ship it in a browser, a mobile app, or anything a user can read. A key acts as you: it creates markets under your account, spends your daily allowance, and earns your XP and HIQ. There is deliberately no CORS header on /api/v1, because a key should never be in a position to be sent from a page.
Your key only works while your subscription is active. If it lapses, the key stops working that day — it is not a standing grant.
Scopes
markets:read
GET endpoints
markets:write
POST endpoints
Both are granted by default. Untick "Allow creating markets" when creating a key to get a read-only one. A request outside its scope returns 403 INSUFFICIENT_SCOPE with the required and granted scopes in details.
POST /api/v1/markets
Creates a market. It is live immediately — there is no approval queue, matching the web app.
Request
The JSON body must be 1 MB or smaller, and the request must include a Content-Length header.
title
string
yes
1–120 characters
description
string
yes
1–5000 characters, used verbatim
options
string[]
yes
2–20; blanks are dropped before counting
marketType
"event" | "opinion" | "multiwinner"
yes
topics
string[]
yes
Non-empty, max 10. Names, not ids. First becomes the primary topic
newTopic
string | null
no
Propose one topic that does not exist yet. See below
filterCountries
string[]
no
Full country names
statisticsCountries
string[]
no
Full country names
estimatedEndDate
ISO 8601 | null
no
Must be in the future, within 5 years. Ignored for opinion
locksAt
ISO 8601 | null
no
Must be ≤ estimatedEndDate
isMultiSelect
boolean
no
Premium+ only
isNsfw
boolean
no
isAnonymous
boolean
no
Response — 201
Idempotency
Send an Idempotency-Key header — any client-generated string up to 255 characters. Strongly recommended. Without one, a request that times out and gets retried creates a second market.
Same key replayed after success → the original
201with an identical body, no second market.Same key while the first request is still in flight →
409 IDEMPOTENCY_CONFLICT.Keys are remembered for 24 hours.
A request that fails releases its key immediately, so you can retry at once.
Topics
Topics are addressed by name, case-insensitively: "NBA", "nba" and "Nba" are the same topic, and "PREMIER LEAGUE" resolves to Premier league.
The first name in topics becomes the market's primary topic — the one shown on the card. The rest are additional tags.
Only the curated vocabulary is accepted. Fetch it from GET /api/v1/topics; do not assume a name works because you saw it on the site, as topics auto-generated on existing markets are not addressable unless Hilo has curated them.
Common alternate names resolve automatically — send Crypto and the market is tagged Cryptocurrency. GET /api/v1/topics lists every such mapping under aliases, and it is the only place to discover them, because where an alias exists there is no error to tell you.
An unknown name returns 400 TOPIC_NOT_FOUND naming the offending values.
Proposing a new topic
If a topic genuinely does not exist, send it as newTopic:
Rules, each of which matters:
topicsmust still carry at least one curated name. A market tagged only with a proper noun nobody browses is invisible; the new topic is additive detail.One per market, and at most 3 per day. Requests proposing a new topic are processed one at a time per user; a concurrent one gets
429 RATE_LIMITEDwith a shortRetry-After.2–30 characters, letters/digits/spaces, not digits-only, no leading digit. A violation is
422 VALIDATION_ERROR, like any other body error.It is resolved through the same chain as
topicsfirst, aliases included — sonewTopic: "cryptocurrency"simply attaches the existingCryptocurrencyand creates nothing.If an uncurated topic already holds the name, the market is attached to that one instead. Nothing is created, and it does not count against your 3/day.
Some names are permanently unavailable because a removed topic once held them. Those return
422 INVALID_TOPIC_NAME— pick a different name.The topic tags this market immediately, but is not addressable by name on your next request until Hilo promotes it, and it will not appear in
GET /api/v1/topics. After creatingTennis, for example, usingtopics: ["Tennis"]on your next market returns400until it is promoted.
GET /api/v1/topics
The curated vocabulary, most-used first, plus the alias map. Cached for one hour.
This endpoint is authoritative. The vocabulary is data, not contract — it grows and shrinks as Hilo curates it, and that is not a breaking change.
GET /api/v1/markets/{idOrSlug}
Reads a market by id or slug. Returns title, description, options, current odds, vote counts, topics, image, resolution state and timestamps. 404 MARKET_NOT_FOUND otherwise.
GET /api/v1/me
Your identity, tier, level/XP/HIQ/MIS, today's market count against your limit, the granted scopes, and the key's id, name and prefix.
It also returns canCreateMarket and, when false, requirements[] — the same shape POST /api/v1/markets returns on a 403. Call this before your first create to confirm your account is eligible without a failed write.
POST /api/v1/markets/{idOrSlug}/image
Attaches an image. multipart/form-data, field name file, max 5 MB. A Content-Length header is required — requests without one are rejected with 411. curl -F and mainstream HTTP clients send it automatically.
The format is detected from the file contents, not its name or declared type — a .png renamed to .webp is accepted, a PDF renamed to .webp is not. Images are re-encoded to WebP, EXIF-rotated and resized to fit 1920×1920.
Replacing an existing image requires being the market's creator.
Limits
Market creation volume is governed by your subscription tier, shared with the web app — using both surfaces does not double your allowance:
Free
1
Premium
5
Premium+
Unlimited
Rate limits on top of that:
Per key
120 / minute
Per IP
300 / minute
Market creation
5 / minute, 200 / day
Market creation (shared with the web UI)
100 / hour
Image upload
20 / hour
New topic
3 / day
A 429 carries Retry-After in seconds.
Eligibility
Beyond the tier limit, market creation requires a minimum account age, level, MIS score and HIQ rating. These are configured platform-wide and apply to the API exactly as they do to the web UI. A 403 REQUIREMENTS_NOT_MET lists each unmet requirement with your current value and the needed one. GET /api/v1/me gives the same answer without a failed write.
Errors
Every error carries a stable machine-readable code:
Branch on code, never on error. Codes are part of the contract; messages are not and may be reworded at any time.
MISSING_API_KEY
401
No Authorization header
INVALID_API_KEY
401
Unknown, revoked or expired
INSUFFICIENT_SCOPE
403
Key lacks the required scope
PREMIUM_REQUIRED
403
Subscription lapsed or never held
PREMIUM_PLUS_REQUIRED
403
isMultiSelect needs Premium+
ACCOUNT_DEACTIVATED
403
ACTION_RESTRICTED
403
Account restricted from this action
DAILY_LIMIT_REACHED
403
Tier's daily market limit hit
REQUIREMENTS_NOT_MET
403
See details.requirements
NOT_MARKET_OWNER
403
Only the creator may replace an image
INVALID_OPTION_COUNT
400
TOPIC_NOT_FOUND
400
See details.topics
INVALID_IMAGE
400
Not a decodable image
MARKET_NOT_FOUND
404
USER_NOT_FOUND
404
IDEMPOTENCY_CONFLICT
409
Same key still in flight
PAYLOAD_TOO_LARGE
413
Create body over 1 MB, or image over 5 MB
UNSUPPORTED_MEDIA_TYPE
415
Expected multipart/form-data
INVALID_TOPIC_NAME
422
newTopic name is permanently unavailable (a removed topic holds it)
VALIDATION_ERROR
422
Request body invalid (411 when Content-Length is missing)
RATE_LIMITED
429
See Retry-After
INTERNAL_ERROR
500
STORAGE_ERROR
502
Image storage unavailable
SERVICE_DISABLED
503
API temporarily disabled
Good to know
descriptionis required and used verbatim. Unlike the web UI, nothing writes one for you.Country fields take full names —
"United States", not"US". An unknown value is rejected rather than silently dropped.Opinion markets always open at equal odds. Seeding your own opening percentages is not available, since it would let you set the odds on your own market.
A topic you mint with
newTopicis not resolvable by name on your next request until Hilo curates it.Photo bonus XP earned via the image endpoint may not be included in the market-created notification's XP total. You still earn it.
One curated topic is unreachable by name —
🏆 2026 World Cupbegins with an emoji and does not survive name normalization.
Versioning
Additive changes ship in place. New response fields, new optional request fields and new endpoints can appear in
v1at any time. Ignore unknown fields.Breaking changes get
/api/v2. Removing or renaming a field, tightening validation, or changing the status code for an existing condition is breaking. Error codes are part of the contract; error messages are not.Deprecation: a removed endpoint keeps serving for 90 days with
DeprecationandSunsetheaders, and key owners using it get one email.The topic vocabulary is data, not contract.
Not in v1
Drafts, voting and resolution, webhooks, listing your own markets, and AI enrichment of submitted markets.
Last updated
Was this helpful?