{ "openapi": "3.0.0", "info": { "title": "PMXT Hosted Router API", "description": "Hosted-only endpoints for cross-venue search, matching, arbitrage, and SQL.", "version": "04d3400" }, "servers": [ { "url": "https://api.pmxt.dev", "description": "Production" } ], "security": [ { "bearerAuth": [] } ], "paths": { "/v0/matched-event-clusters": { "get": { "summary": "Fetch matched event clusters", "description": "Returns connected clusters of semantically matched events across venues. Use this endpoint to browse or anchor event-level matches while preserving each venue's child markets.\n", "tags": [ "MatchedMarkets" ], "parameters": [ { "in": "query", "name": "eventId", "schema": { "type": "string" }, "description": "Anchor the response to a specific event ID." }, { "in": "query", "name": "slug", "schema": { "type": "string" }, "description": "Anchor the response to a specific event slug." }, { "in": "query", "name": "url", "schema": { "type": "string" }, "description": "Anchor the response to a specific event URL." }, { "in": "query", "name": "query", "schema": { "type": "string" }, "description": "Text search across cluster titles.", "example": "Satoshi" }, { "in": "query", "name": "category", "schema": { "type": "string" }, "description": "Filter both sides of matched edges by event category.", "example": "Crypto" }, { "in": "query", "name": "relations", "schema": { "type": "string", "default": "identity" }, "description": "Comma-separated relation filter. Valid values: identity (same resolution), subset (A yes implies B yes), superset (B yes implies A yes), overlap (some shared scenarios), and disjoint (mutually exclusive). Defaults to identity. For subset and superset, direction follows the pairwise edge direction returned in rawMatches when includeRawMatches=true.\n", "example": "identity" }, { "in": "query", "name": "relation", "schema": { "type": "string", "enum": [ "identity", "subset", "superset", "overlap", "disjoint" ], "default": "identity" }, "description": "Single relation filter. Alias for relations.", "example": "identity" }, { "in": "query", "name": "minConfidence", "schema": { "type": "number", "minimum": 0, "maximum": 1, "default": 0 } }, { "in": "query", "name": "venues", "schema": { "type": "string" }, "description": "Comma-separated venue allow-list.", "example": "polymarket,kalshi" }, { "in": "query", "name": "excludeVenues", "schema": { "type": "string" }, "description": "Comma-separated venue deny-list." }, { "in": "query", "name": "minVenues", "schema": { "type": "integer", "minimum": 0 }, "description": "Minimum number of venues required in a cluster." }, { "in": "query", "name": "withOrderbook", "schema": { "type": "boolean", "default": false }, "description": "Require at least one live orderbook on each matched edge." }, { "in": "query", "name": "updatedSince", "schema": { "type": "string", "format": "date-time" }, "description": "Only include matches updated after this timestamp." }, { "in": "query", "name": "includeRawMatches", "schema": { "type": "boolean", "default": false }, "description": "Include the pairwise match edges used to build each cluster." }, { "in": "query", "name": "sort", "schema": { "type": "string", "enum": [ "volume", "confidence" ], "default": "volume" } }, { "in": "query", "name": "limit", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 50 } }, { "in": "query", "name": "offset", "schema": { "type": "integer", "minimum": 0, "default": 0 } }, { "in": "query", "name": "edgeLimit", "schema": { "type": "integer", "minimum": 1 }, "description": "Maximum number of pairwise edges to scan before clustering." } ], "responses": { "200": { "description": "Matched event clusters." } }, "x-codeSamples": [ { "lang": "python", "label": "Python", "source": "import pmxt\n\nrouter = pmxt.Router(pmxt_api_key=\"YOUR_PMXT_API_KEY\")\nclusters = router.fetch_matched_event_clusters(\n query=\"Satoshi\",\n relation=\"identity\",\n min_venues=2,\n include_raw_matches=True,\n limit=5,\n)\n\nfor cluster in clusters:\n venues = [event.source_exchange for event in cluster.events]\n print(cluster.canonical_title, venues)\n" }, { "lang": "javascript", "label": "TypeScript", "source": "import { Router } from \"pmxtjs\";\n\nconst router = new Router({ pmxtApiKey: \"YOUR_PMXT_API_KEY\" });\n\nasync function main() {\n const clusters = await router.fetchMatchedEventClusters({\n query: \"Satoshi\",\n relation: \"identity\",\n minVenues: 2,\n includeRawMatches: true,\n limit: 5,\n });\n\n for (const cluster of clusters) {\n console.log(\n cluster.canonicalTitle,\n cluster.events.map((event) => event.sourceExchange),\n );\n }\n}\n\nmain();\n" }, { "lang": "bash", "label": "curl", "source": "curl -G \"https://api.pmxt.dev/v0/matched-event-clusters\" \\\n -H \"Authorization: Bearer $PMXT_API_KEY\" \\\n --data-urlencode \"query=Satoshi\" \\\n --data-urlencode \"relation=identity\" \\\n --data-urlencode \"minVenues=2\" \\\n --data-urlencode \"includeRawMatches=true\" \\\n --data-urlencode \"limit=5\"\n" } ], "operationId": "getV0Matched-event-clusters" } }, "/v0/matched-market-clusters": { "get": { "summary": "Fetch matched market clusters", "description": "Returns connected clusters of semantically matched markets across venues. Use this endpoint to browse or anchor market-level matches without flattening the response into pairwise rows.\n", "tags": [ "MatchedMarkets" ], "parameters": [ { "in": "query", "name": "marketId", "schema": { "type": "string" }, "description": "Anchor the response to a specific market ID." }, { "in": "query", "name": "slug", "schema": { "type": "string" }, "description": "Anchor the response to a specific market slug." }, { "in": "query", "name": "url", "schema": { "type": "string" }, "description": "Anchor the response to a specific market URL." }, { "in": "query", "name": "query", "schema": { "type": "string" }, "description": "Text search across cluster titles.", "example": "Satoshi" }, { "in": "query", "name": "category", "schema": { "type": "string" }, "description": "Filter both sides of matched edges by market category.", "example": "Crypto" }, { "in": "query", "name": "relations", "schema": { "type": "string", "default": "identity" }, "description": "Comma-separated relation filter. Valid values: identity (same resolution), subset (A yes implies B yes), superset (B yes implies A yes), overlap (some shared scenarios), and disjoint (mutually exclusive). Defaults to identity. For subset and superset, direction follows the pairwise edge direction returned in rawMatches when includeRawMatches=true.\n", "example": "identity" }, { "in": "query", "name": "relation", "schema": { "type": "string", "enum": [ "identity", "subset", "superset", "overlap", "disjoint" ], "default": "identity" }, "description": "Single relation filter. Alias for relations.", "example": "identity" }, { "in": "query", "name": "minConfidence", "schema": { "type": "number", "minimum": 0, "maximum": 1, "default": 0 } }, { "in": "query", "name": "venues", "schema": { "type": "string" }, "description": "Comma-separated venue allow-list.", "example": "polymarket,kalshi" }, { "in": "query", "name": "excludeVenues", "schema": { "type": "string" }, "description": "Comma-separated venue deny-list." }, { "in": "query", "name": "minVenues", "schema": { "type": "integer", "minimum": 0 }, "description": "Minimum number of venues required in a cluster." }, { "in": "query", "name": "withOrderbook", "schema": { "type": "boolean", "default": false }, "description": "Require at least one live orderbook on each matched edge." }, { "in": "query", "name": "updatedSince", "schema": { "type": "string", "format": "date-time" }, "description": "Only include matches updated after this timestamp." }, { "in": "query", "name": "includeRawMatches", "schema": { "type": "boolean", "default": false }, "description": "Include the pairwise match edges used to build each cluster." }, { "in": "query", "name": "sort", "schema": { "type": "string", "enum": [ "volume", "confidence" ], "default": "volume" } }, { "in": "query", "name": "limit", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 50 } }, { "in": "query", "name": "offset", "schema": { "type": "integer", "minimum": 0, "default": 0 } }, { "in": "query", "name": "edgeLimit", "schema": { "type": "integer", "minimum": 1 }, "description": "Maximum number of pairwise edges to scan before clustering." } ], "responses": { "200": { "description": "Matched market clusters." } }, "x-codeSamples": [ { "lang": "python", "label": "Python", "source": "import pmxt\n\nrouter = pmxt.Router(pmxt_api_key=\"YOUR_PMXT_API_KEY\")\nclusters = router.fetch_matched_market_clusters(\n query=\"Satoshi\",\n relation=\"identity\",\n min_venues=2,\n include_raw_matches=True,\n limit=5,\n)\n\nfor cluster in clusters:\n venues = [market.source_exchange for market in cluster.markets]\n print(cluster.canonical_title, venues)\n" }, { "lang": "javascript", "label": "TypeScript", "source": "import { Router } from \"pmxtjs\";\n\nconst router = new Router({ pmxtApiKey: \"YOUR_PMXT_API_KEY\" });\n\nasync function main() {\n const clusters = await router.fetchMatchedMarketClusters({\n query: \"Satoshi\",\n relation: \"identity\",\n minVenues: 2,\n includeRawMatches: true,\n limit: 5,\n });\n\n for (const cluster of clusters) {\n console.log(\n cluster.canonicalTitle,\n cluster.markets.map((market) => market.sourceExchange),\n );\n }\n}\n\nmain();\n" }, { "lang": "bash", "label": "curl", "source": "curl -G \"https://api.pmxt.dev/v0/matched-market-clusters\" \\\n -H \"Authorization: Bearer $PMXT_API_KEY\" \\\n --data-urlencode \"query=Satoshi\" \\\n --data-urlencode \"relation=identity\" \\\n --data-urlencode \"minVenues=2\" \\\n --data-urlencode \"includeRawMatches=true\" \\\n --data-urlencode \"limit=5\"\n" } ], "operationId": "getV0Matched-market-clusters" } }, "/v0/sql": { "post": { "summary": "Execute a read-only SQL query against ClickHouse", "tags": [ "SQL" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "query" ], "properties": { "query": { "type": "string", "example": "SELECT * FROM markets LIMIT 10" } } } } } }, "responses": { "200": { "description": "Query executed successfully", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "type": "object" }, "example": [ { "column1": "value1" } ] }, "meta": { "type": "object", "properties": { "columns": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "type": { "type": "string" } } }, "example": [ { "name": "column1", "type": "String" } ] }, "rows": { "type": "integer", "example": 1 }, "statistics": { "type": "object", "properties": { "elapsed": { "type": "number", "example": 0.005 }, "rows_read": { "type": "integer", "example": 100 }, "bytes_read": { "type": "integer", "example": 5000 } } } } } } } } } }, "400": { "description": "Invalid or disallowed query", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string", "example": "query_error" }, "message": { "type": "string" } } } } } }, "403": { "description": "Enterprise plan required", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string", "example": "sql_access_denied" }, "message": { "type": "string", "example": "SQL query access requires an Enterprise plan" } } } } } }, "408": { "description": "Query timed out", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string", "example": "query_timeout" }, "message": { "type": "string", "example": "Query exceeded the maximum execution time (5s)" } } } } } }, "503": { "description": "ClickHouse not configured", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string", "example": "service_unavailable" }, "message": { "type": "string", "example": "SQL query service is not available" } } } } } } }, "operationId": "postV0Sql" } } }, "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Required when calling the hosted API directly (curl, requests, fetch). SDK users pass credentials via constructor params instead." } }, "schemas": { "UnifiedOutcome": { "type": "object", "required": [ "outcomeId", "marketId", "label" ], "properties": { "outcomeId": { "type": "string" }, "marketId": { "type": "string" }, "label": { "type": "string" }, "price": { "type": "number", "nullable": true }, "priceChange24h": { "type": "number", "nullable": true }, "bestBid": { "type": "number", "nullable": true }, "bestAsk": { "type": "number", "nullable": true }, "metadata": { "type": "object", "nullable": true } } }, "UnifiedMarket": { "type": "object", "required": [ "marketId", "title", "slug", "outcomes" ], "properties": { "marketId": { "type": "string" }, "eventId": { "type": "string", "nullable": true }, "title": { "type": "string" }, "slug": { "type": "string" }, "description": { "type": "string", "nullable": true }, "url": { "type": "string", "nullable": true }, "image": { "type": "string", "nullable": true }, "category": { "type": "string", "nullable": true }, "tags": { "type": "array", "items": { "type": "string" }, "nullable": true }, "volume": { "type": "number", "nullable": true }, "volume24h": { "type": "number" }, "liquidity": { "type": "number" }, "openInterest": { "type": "number", "nullable": true }, "resolutionDate": { "type": "string", "format": "date-time", "nullable": true }, "tickSize": { "type": "number", "nullable": true }, "status": { "type": "string", "nullable": true }, "contractAddress": { "type": "string", "nullable": true }, "outcomes": { "type": "array", "items": { "$ref": "#/components/schemas/UnifiedOutcome" } } } }, "UnifiedEvent": { "type": "object", "required": [ "id", "title", "slug", "markets" ], "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string", "nullable": true }, "slug": { "type": "string" }, "markets": { "type": "array", "items": { "$ref": "#/components/schemas/UnifiedMarket" } }, "volume24h": { "type": "number", "nullable": true }, "volume": { "type": "number", "nullable": true }, "url": { "type": "string", "nullable": true }, "image": { "type": "string", "nullable": true }, "category": { "type": "string", "nullable": true }, "tags": { "type": "array", "items": { "type": "string" }, "nullable": true } } }, "ListMeta": { "type": "object", "required": [ "count", "limit", "offset" ], "properties": { "count": { "type": "integer" }, "limit": { "type": "integer" }, "offset": { "type": "integer" } } }, "ErrorResponse": { "type": "object", "required": [ "error" ], "properties": { "error": { "type": "string" } } }, "RateLimitError": { "type": "object", "required": [ "error", "plan", "limit", "used" ], "properties": { "error": { "type": "string" }, "plan": { "type": "string" }, "limit": { "type": "integer" }, "used": { "type": "integer" }, "window": { "type": "string" } } } } } }