Developers and agents
For AI agents and scripts
Everything on IndiePrime works over plain JSON. One key, one request, one live game page. Nothing to scrape and no forms to click.
Quick start
-
Create an API key
Sign in and open your studio. Press Create key. Copy the key once. It starts with
ipk_. You cannot see it again. -
Create a listing
Send one POST. Creators join for $10 once. Non-member games are saved as drafts until the creator joins. The response includes
payment_requiredandcheckout_url. The creator opens that link, signs in with the same account, and joins once. Membership covers additional games. The bundle includes 2,000 Asset Yard credits. API keys cannot make payments.curl -X POST https://indieprime.com/api/v1/games \ -H "Authorization: Bearer ipk_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Neon Drift", "tagline": "Race a comet through a neon city.", "genres": ["racing", "arcade"], "cover_url": "https://you.github.io/neon-drift/cover.png", "play_url": "https://you.github.io/neon-drift/", "play_mode": "embed", "price_cents": 0, "status": "published" }'import requests resp = requests.post( "https://indieprime.com/api/v1/games", headers={"Authorization": "Bearer ipk_YOUR_KEY"}, json={ "title": "Neon Drift", "tagline": "Race a comet through a neon city.", "genres": ["racing", "arcade"], "cover_url": "https://you.github.io/neon-drift/cover.png", "play_url": "https://you.github.io/neon-drift/", "play_mode": "embed", "price_cents": 0, "status": "published", }, timeout=30, ) resp.raise_for_status() print(resp.json()["game"]["url"])const res = await fetch("https://indieprime.com/api/v1/games", { method: "POST", headers: { "Authorization": "Bearer ipk_YOUR_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ title: "Neon Drift", tagline: "Race a comet through a neon city.", genres: ["racing", "arcade"], cover_url: "https://you.github.io/neon-drift/cover.png", play_url: "https://you.github.io/neon-drift/", play_mode: "embed", price_cents: 0, status: "published", }), }); const data = await res.json(); if (!res.ok) throw new Error(JSON.stringify(data)); console.log(data.game.url); -
Upload a web build (optional)
Zip your HTML5 build with
index.htmlat the top. Send the raw zip as the body. Up to 100 MB. The game page then gets a PLAY NOW button.curl -X PUT https://indieprime.com/api/v1/games/neon-drift/build \ -H "Authorization: Bearer ipk_YOUR_KEY" \ -H "Content-Type: application/zip" \ --data-binary @build.zip
import requests with open("build.zip", "rb") as f: resp = requests.put( "https://indieprime.com/api/v1/games/neon-drift/build", headers={ "Authorization": "Bearer ipk_YOUR_KEY", "Content-Type": "application/zip", }, data=f, timeout=300, ) resp.raise_for_status() print(resp.json())import { readFile } from "node:fs/promises"; const zip = await readFile("build.zip"); const res = await fetch("https://indieprime.com/api/v1/games/neon-drift/build", { method: "PUT", headers: { "Authorization": "Bearer ipk_YOUR_KEY", "Content-Type": "application/zip", }, body: zip, }); console.log(res.status, await res.json());
Copy this prompt into your agent
Paste this into Claude, ChatGPT, Cursor or any agent that can make HTTP requests. Fill in the brackets.
Read https://indieprime.com/llms.txt and follow it. Then list my game on IndiePrime. Game name: [YOUR GAME NAME] Where it lives: [LINK TO YOUR GAME PAGE OR WEB BUILD] My API key: [ipk_ KEY FROM https://indieprime.com/studio] Rules: - Use POST /api/v1/games with the Authorization: Bearer header. - Write a tagline under 160 characters and a plain description. - Every URL must use https. - Set status to published. - If the API returns 422, read the "fields" object, fix those fields and try again. - When you finish, print the public game URL.
Listing schema
Send these fields as JSON to create or update a game. Only title is required. Every URL must be https. Store links must be on that store's own domain.
| Field | Type | Required | Rules |
|---|---|---|---|
title | string | yes | The name of the game. |
slug | string | no | The address: /g/slug. Made from the title if you skip it. Lowercase letters, numbers and hyphens. |
tagline | string | no | Max 160 characters. |
description | string | no | Max 8000 characters. Line breaks are kept. |
developer_name | string | no | You or your studio. |
made_with | string | no | Engine or tools, for example Godot. |
genres | string[] | no | Max 5. Allowed values: action adventure arcade card casual horror idle management platformer puzzle racing roguelike rpg shooter simulation space sports stealth strategy survival tactics tower-defense visual-novel voxel educational multiplayer |
tags | string[] | no | Max 8 free-form tags. |
cover_url | https URL | no | A 16:9 image works best. |
screenshots | https URL[] | no | Up to 8 images. |
play_url | https URL | no | Where the game plays in a browser. |
play_mode | string | no | embed plays on the page. external opens a new tab. none shows no play button. |
price_cents | integer | no | 0 to 100000. Zero means free. US dollars in cents. |
pay_what_you_want | boolean | no | Let the player pick the price. |
pay_url | https URL | no | Your own checkout link. IndiePrime has a 10% revenue share. |
downloads | object | no | Keys: windows mac linux android . Values are https URLs. |
stores | object | no | Keys: steam ios google_play amazon itch epic gog microsoft humble other . Values are https URLs on the store's own domain. |
status | string | no | published or draft. |
{
"title": "Neon Drift",
"slug": "neon-drift",
"tagline": "Race a comet through a neon city.",
"description": "Drift, boost and outrun the storm.\nNew tracks every week.",
"developer_name": "Comet Club",
"made_with": "Godot",
"genres": ["racing", "arcade"],
"tags": ["neon", "short runs"],
"cover_url": "https://you.github.io/neon-drift/cover.png",
"screenshots": ["https://you.github.io/neon-drift/s1.png"],
"play_url": "https://you.github.io/neon-drift/",
"play_mode": "embed",
"price_cents": 499,
"pay_what_you_want": false,
"pay_url": "https://buy.stripe.com/your-link",
"downloads": {"windows": "https://you.itch.io/neon-drift/win.zip"},
"stores": {"steam": "https://store.steampowered.com/app/123456"},
"status": "published"
}
Endpoints
Base URL: https://indieprime.com. Send your key as Authorization: Bearer ipk_....
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/games | Search and list games. Query: q, genre, platform, price, sort, page, per_page. Public. |
| GET | /api/v1/games/{slug} | Read one game. Public. |
| POST | /api/v1/games | Create a game. Returns 201 and {"game": {...}}. |
| PATCH | /api/v1/games/{slug} | Change some fields of your game. Returns 200. |
| DELETE | /api/v1/games/{slug} | Delete your game. |
| PUT | /api/v1/games/{slug}/build | Upload a web build. Body is the raw zip. Header Content-Type: application/zip. Max 100 MB. |
| POST | /api/v1/import | Send {"url": "https://..."}. Get back a suggestion object you can review and post. |
| GET | /api/v1/mine | List your own games, drafts included. |
| POST | /api/v1/keys | Create an API key. Body {"name": "my agent"}. The secret shows once. |
| GET | /api/v1/keys | List your keys. Only a prefix is shown. |
| DELETE | /api/v1/keys/{id} | Revoke a key. |
Machine-readable files
| File | For |
|---|---|
/llms.txt | A short guide written for language models. |
/api/v1/openapi.json | The full OpenAPI description. Point a tool generator at it. |
/.well-known/indieprime.json | Discovery file. Lists the API base URL and the docs. |
Errors
A bad request returns 422. The body has a short error and a fields object. Each key is a field name. Nested fields use dots, like stores.steam. Fix those fields and send the request again.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": "Some fields need a fix.",
"fields": {
"title": "Title is required.",
"stores.steam": "Steam links must be on store.steampowered.com.",
"downloads.windows": "Use an https link."
}
}
Rate limits
- 30 writes per minute per account. Writes are POST, PUT, PATCH and DELETE.
- Reads of public games have no set limit. Be kind and cache what you can.
- If you go over, wait a minute and try again.
MCP and tool-use friendly
Predictable JSON
Every response is JSON. Success returns the object. Failure returns error and, for bad input, fields. No HTML surprises.
Safe to retry
Updates go to a slug, so sending the same PATCH twice gives the same game. An agent can repeat a step without making a duplicate.
Self-describing
Use /api/v1/openapi.json to generate tools for an MCP server or a function-calling agent in minutes.
One key per agent
Make a key for each agent. Revoke it in your studio when the job is done.