crob.at REST API
Public REST API to create and retrieve Pokémon Showdown team links, generate random teams, and access sample teams and authenticated Showdown helpers.
Public REST API to create and retrieve Pokémon Showdown team links, generate random teams, and access sample teams and authenticated Showdown helpers.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/crobat-rest-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
{
"openapi": "3.1.0",
"info": {
"title": "crob.at API",
"version": "1.0.0",
"description": "Create, retrieve, and generate competitive Pokemon Showdown teams, read reference data, and use optional crob.at session helpers.",
"termsOfService": "https://crob.at/api#terms",
"contact": {
"name": "crob.at API support",
"url": "https://crob.at/contact",
"email": "admin@crob.at"
}
},
"externalDocs": {
"description": "Human-readable API documentation",
"url": "https://crob.at/api"
},
"servers": [
{
"url": "https://crob.at",
"description": "Production"
}
],
"security": [],
"tags": [
{
"name": "Teams",
"description": "Create and retrieve shareable Pokemon Showdown teams."
},
{
"name": "Generation",
"description": "Generate and optionally save competitive random teams."
},
{
"name": "Reference",
"description": "Read sample teams and Pokemon reference data."
},
{
"name": "Account",
"description": "Optional same-origin helpers that use a crob.at browser session."
},
{
"name": "Feedback",
"description": "Send product feedback to the crob.at team."
}
],
"paths": {
"/api/team/{slug}": {
"get": {
"tags": ["Teams"],
"summary": "Retrieve a team",
"description": "Returns a saved single-team or multi-team paste by its public slug.",
"operationId": "getTeamBySlug",
"parameters": [
{
"name": "slug",
"in": "path",
"required": true,
"description": "The team's public slug.",
"schema": {
"type": "string",
"minLength": 1
}
}
],
"responses": {
"200": {
"description": "The saved team.",
"headers": {
"Cache-Control": {
"description": "Public response cache policy.",
"schema": { "type": "string" }
}
},
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Team" }
}
}
},
"404": { "$ref": "#/components/responses/NotFound" }
}
}
},
"/api/team/{slug}/{teamSlug}": {
"get": {
"tags": ["Teams"],
"summary": "Retrieve one team from a multi-team paste",
"description": "Returns one named team from a saved multi-team paste. Use the child slug shown in the shared URL or derived from the team name.",
"operationId": "getTeamInPasteBySlug",
"parameters": [
{
"name": "slug",
"in": "path",
"required": true,
"description": "The multi-team paste's public slug.",
"schema": { "type": "string", "minLength": 1 }
},
{
"name": "teamSlug",
"in": "path",
"required": true,
"description": "The selected team's slug within the paste.",
"schema": { "type": "string", "minLength": 1 }
}
],
"responses": {
"200": {
"description": "The selected team.",
"headers": {
"Cache-Control": {
"description": "Public response cache policy.",
"schema": { "type": "string" }
}
},
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/TeamInPaste" }
}
}
},
"404": { "$ref": "#/components/responses/NotFound" }
}
}
},
"/api/team": {
"post": {
"tags": ["Teams"],
"summary": "Create a team",
"description": "Creates a shareable team from one or more Pokemon Showdown exports. No account or API key is required.",
"operationId": "createTeam",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/CreateTeamRequest" }
}
}
},
"responses": {
"201": {
"description": "The team was created.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/CreatedTeam" }
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"429": { "$ref": "#/components/responses/RateLimited" },
"500": { "$ref": "#/components/responses/ServerError" }
}
}
},
"/api/random-team/{format}": {
"get": {
"tags": ["Generation"],
"summary": "Generate a random team",
"description": "Generates a usage-weighted competitive team without saving it.",
"operationId": "generateRandomTeam",
"parameters": [
{ "$ref": "#/components/parameters/Format" }
],
"responses": {
"200": {
"description": "A generated Pokemon Showdown team and its rendered card HTML.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/GeneratedTeam" }
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"404": { "$ref": "#/components/responses/NotFound" },
"502": {
"description": "The upstream usage-data source could not be read.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
}
}
}
},
"/api/random-team/{format}/save": {
"post": {
"tags": ["Generation"],
"summary": "Save a generated team",
"description": "Creates an unlisted permanent URL for a previously generated team. Reuse one idempotencyKey when a save request is retried.",
"operationId": "saveRandomTeam",
"parameters": [
{ "$ref": "#/components/parameters/Format" }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["teamText"],
"properties": {
"teamText": {
"type": "string",
"minLength": 1,
"maxLength": 500000,
"description": "A Pokemon Showdown team export returned by the generation endpoint."
},
"idempotencyKey": {
"type": "string",
"minLength": 16,
"maxLength": 64,
"pattern": "^[A-Za-z0-9_-]+$",
"description": "A stable retry key. Reuse it only for the same generated team."
}
}
}
}
}
},
"responses": {
"201": {
"description": "The generated team was saved.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/CreatedTeam" }
}
}
},
"200": {
"description": "A retry returned the team that was already saved with this idempotency key.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/CreatedTeam" }
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"429": { "$ref": "#/components/responses/RateLimited" },
"500": { "$ref": "#/components/responses/ServerError" }
}
}
},
"/api/samples/{tier}": {
"get": {
"tags": ["Reference"],
"summary": "List sample teams",
"description": "Returns the available Smogon sample teams for a competitive tier.",
"operationId": "listSampleTeams",
"parameters": [
{
"name": "tier",
"in": "path",
"required": true,
"description": "An alphanumeric Showdown format identifier, such as gen9ou.",
"schema": {
"type": "string",
"pattern": "^[a-z0-9]+$"
}
}
],
"responses": {
"200": {
"description": "Sample teams, or an empty array when none are available.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/SampleTeam" }
}
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" }
}
}
},
"/api/type-chart-data": {
"get": {
"tags": ["Reference"],
"summary": "Read Pokemon type data",
"description": "Returns Pokemon names and their types for the interactive type chart.",
"operationId": "getTypeChartData",
"responses": {
"200": {
"description": "Pokemon names paired with one or two types.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"prefixItems": [
{ "type": "string" },
{
"type": "array",
"minItems": 1,
"maxItems": 2,
"items": { "type": "string" }
}
],
"items": {}
}
}
}
}
}
}
}
},
"/api/me": {
"get": {
"tags": ["Account"],
"summary": "Read the current user",
"description": "Returns the current crob.at user when a valid browser session cookie is present, otherwise returns a null user.",
"operationId": "getCurrentUser",
"security": [
{ "cookieSession": [] },
{}
],
"responses": {
"200": {
"description": "The current user or null.",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["user"],
"properties": {
"user": {
"oneOf": [
{ "$ref": "#/components/schemas/CurrentUser" },
{ "type": "null" }
]
}
}
}
}
}
}
}
}
},
"/api/feedback": {
"post": {
"tags": ["Feedback"],
"summary": "Send feedback",
"description": "Sends a feedback message with optional contact details.",
"operationId": "sendFeedback",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["message"],
"properties": {
"message": {
"type": "string",
"minLength": 1,
"maxLength": 2000,
"pattern": "\\S",
"description": "The trimmed message must contain a non-whitespace character and be at most 2,000 characters."
},
"contact": {
"type": "string",
"description": "Optional contact details. Values longer than 100 characters are truncated."
}
}
}
}
}
},
"responses": {
"201": {
"description": "Feedback was accepted.",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["success"],
"properties": {
"success": { "const": true }
}
}
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"429": { "$ref": "#/components/responses/RetryAfterRateLimited" },
"500": { "$ref": "#/components/responses/ServerError" }
}
}
},
"/api/showdown/assertion": {
"post": {
"tags": ["Account"],
"summary": "Start browser-side Showdown assertion generation",
"description": "Redirects the signed-in user's browser to Pokemon Showdown so the challstr-specific assertion is generated from the user's network rather than crob.at's server.",
"operationId": "createShowdownAssertion",
"security": [
{ "cookieSession": [] }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["challstr"],
"properties": {
"challstr": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\r\\n]+$",
"description": "A Pokemon Showdown challenge string without line breaks."
}
}
}
}
}
},
"responses": {
"303": {
"description": "Redirect to Pokemon Showdown's OAuth assertion endpoint.",
"headers": {
"Location": {
"required": true,
"schema": {
"type": "string",
"format": "uri"
}
}
}
},
"400": { "$ref": "#/components/responses/BadRequest" },
"401": {
"description": "A linked, authenticated Showdown account is required.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
},
"503": {
"description": "Showdown OAuth is not configured.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"cookieSession": {
"type": "apiKey",
"in": "cookie",
"name": "session",
"description": "Optional crob.at browser session cookie. Public endpoints do not require it."
}
},
"parameters": {
"Format": {
"name": "format",
"in": "path",
"required": true,
"description": "An alphanumeric Showdown format identifier, such as gen9ou.",
"schema": {
"type": "string",
"pattern": "^[a-z0-9]+$"
}
}
},
"responses": {
"BadRequest": {
"description": "The request body or route parameter is invalid.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
},
"NotFound": {
"description": "The requested resource was not found.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
},
"RateLimited": {
"description": "The per-client write limit was exceeded.",
"headers": {
"Retry-After": {
"description": "Seconds until the request may be retried.",
"schema": { "type": "integer", "minimum": 1 }
},
"X-RateLimit-Limit": {
"description": "Requests permitted in the current window.",
"schema": { "type": "integer" }
},
"X-RateLimit-Remaining": {
"description": "Requests remaining in the current window.",
"schema": { "type": "integer" }
},
"X-RateLimit-Reset": {
"description": "ISO 8601 timestamp when the window resets.",
"schema": { "type": "string", "format": "date-time" }
}
},
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
},
"RetryAfterRateLimited": {
"description": "The per-client write limit was exceeded.",
"headers": {
"Retry-After": {
"description": "Seconds until the request may be retried.",
"schema": { "type": "integer", "minimum": 1 }
}
},
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
},
"ServerError": {
"description": "The request could not be completed because of a server error.",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Error" }
}
}
}
},
"schemas": {
"Error": {
"type": "object",
"required": ["error"],
"properties": {
"error": { "type": "string" },
"message": { "type": "string" },
"retryAfter": { "type": "integer", "minimum": 1 }
}
},
"TeamPaste": {
"type": "object",
"required": ["paste"],
"properties": {
"format": { "type": ["string", "null"] },
"name": { "type": ["string", "null"] },
"paste": { "type": "string" }
}
},
"Team": {
"type": "object",
"required": ["slug", "name", "author", "description", "public", "image", "source_url", "created_at", "views", "is_multi", "teams"],
"properties": {
"slug": { "type": "string" },
"name": { "type": ["string", "null"] },
"author": { "type": ["string", "null"] },
"description": { "type": ["string", "null"] },
"public": {
"type": "boolean",
"description": "Whether the team may appear in the public gallery and search."
},
"image": { "type": "string", "format": "uri" },
"source_url": { "type": ["string", "null"], "format": "uri" },
"created_at": { "type": "string" },
"views": { "type": "integer", "minimum": 0 },
"is_multi": { "type": "boolean" },
"teams": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/components/schemas/TeamPaste" }
}
}
},
"TeamInPaste": {
"type": "object",
"required": ["paste_slug", "slug", "format", "name", "paste", "url", "image"],
"properties": {
"paste_slug": { "type": "string", "description": "The parent multi-team paste's public slug." },
"slug": { "type": "string", "description": "The selected team's slug within the parent paste." },
"format": { "type": "string" },
"name": { "type": "string" },
"paste": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"image": { "type": "string", "format": "uri" }
}
},
"CreateTeamRequest": {
"type": "object",
"required": ["teams"],
"properties": {
"name": {
"type": "string",
"description": "Optional team name. Values longer than 100 characters are truncated."
},
"author": {
"type": "string",
"description": "Optional author. Values longer than 50 characters are truncated."
},
"description": {
"type": "string",
"description": "Optional description. The trimmed value is truncated to 1,000 characters."
},
"public": {
"type": "boolean",
"default": false,
"description": "Whether the team may appear in the public gallery."
},
"teams": {
"type": "array",
"minItems": 1,
"description": "One or more teams. The combined normalized Showdown export must not exceed 500,000 characters.",
"items": {
"type": "object",
"required": ["paste"],
"properties": {
"name": {
"type": "string",
"description": "Optional team name. Values longer than 100 characters are truncated."
},
"format": {
"type": "string",
"description": "A supported Pokemon Showdown format ID or name. The server normalizes names to IDs."
},
"paste": { "type": "string", "minLength": 1, "maxLength": 500000 }
}
}
}
}
},
"CreatedTeam": {
"type": "object",
"required": ["slug", "url", "image"],
"properties": {
"slug": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"image": { "type": "string", "format": "uri" },
"management_token": { "type": "string", "minLength": 48, "maxLength": 48, "description": "Private creator capability returned by POST /api/team. Store it securely to change visibility later; do not include it in shared team URLs." }
}
},
"GeneratedTeam": {
"type": "object",
"required": ["teamText", "statsDate", "cardsHtml"],
"properties": {
"teamText": { "type": "string" },
"statsDate": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}$" },
"cardsHtml": { "type": "string" }
}
},
"SampleTeam": {
"type": "object",
"required": ["slug", "name", "author", "tier", "views", "created_at", "url", "image", "source_url"],
"properties": {
"slug": { "type": "string" },
"name": { "type": ["string", "null"] },
"author": { "type": ["string", "null"] },
"tier": { "type": ["string", "null"] },
"views": { "type": "integer", "minimum": 0 },
"created_at": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"image": { "type": "string", "format": "uri" },
"source_url": { "type": ["string", "null"], "format": "uri" }
}
},
"CurrentUser": {
"type": "object",
"required": ["username", "email", "ps_username", "team_count", "favorite_count"],
"properties": {
"username": { "type": "string" },
"email": { "type": ["string", "null"], "format": "email" },
"ps_username": { "type": ["string", "null"] },
"team_count": { "type": "integer", "minimum": 0 },
"favorite_count": { "type": "integer", "minimum": 0 }
}
}
}
}
}