{"openapi":"3.1.0","info":{"title":"ExactScore AI API","version":"1.0.0","summary":"AI football predictions (exact score, goalscorers) and their graded track record.","description":"Free, read-only API with every ExactScore AI prediction: exact scoreline, tipped goalscorers and reasoning published before kickoff, then the real result and the grade. No authentication. Errors use application/problem+json (RFC 9457).","contact":{"name":"Simone Ruggiero","email":"simone.ruggiero97@gmail.com","url":"https://exactscoreai.com/contact/"}},"externalDocs":{"description":"Developer docs","url":"https://exactscoreai.com/docs/"},"servers":[{"url":"https://exactscoreai.com"}],"security":[],"tags":[{"name":"predictions","description":"Predictions, results and statistics"}],"paths":{"/api/v1/info":{"get":{"operationId":"getSiteInfo","tags":["predictions"],"summary":"Site information","description":"Name, prediction model, last data update, contact and links to docs, OpenAPI and MCP.","responses":{"200":{"description":"Site information","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteInfo"}}}},"429":{"description":"Rate limit exceeded. Retry after the number of seconds in Retry-After.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/competitions":{"get":{"operationId":"listCompetitions","tags":["predictions"],"summary":"List competitions","description":"Competitions in the catalog. covered=true when the site already has predictions for it.","responses":{"200":{"description":"Competitions","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompetitionList"}}}},"429":{"description":"Rate limit exceeded. Retry after the number of seconds in Retry-After.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/predictions":{"get":{"operationId":"listPredictions","tags":["predictions"],"summary":"List predictions","description":"Predictions with the predicted score and goalscorers. status=pending returns upcoming calls soonest first; otherwise newest kickoff first.","parameters":[{"name":"competition","in":"query","required":false,"description":"Competition key or slug from /api/v1/competitions (e.g. world-cup or world-cup-2026).","schema":{"type":"string","maxLength":60}},{"name":"status","in":"query","required":false,"description":"pending = not played yet, settled = graded against the real result.","schema":{"type":"string","enum":["pending","settled"]}},{"name":"limit","in":"query","required":false,"description":"Page size.","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","required":false,"description":"Number of items to skip.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A page of predictions","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionList"}}}},"400":{"description":"Invalid query parameter.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. Retry after the number of seconds in Retry-After.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/predictions/{id}":{"get":{"operationId":"getPrediction","tags":["predictions"],"summary":"Get one prediction","description":"One prediction with reasoning, real scorers, grade and every revision made before kickoff.","parameters":[{"name":"id","in":"path","required":true,"description":"Prediction id from /api/v1/predictions.","schema":{"type":"string","maxLength":120}}],"responses":{"200":{"description":"The prediction","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Prediction"}}}},"404":{"description":"No prediction with this id.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. Retry after the number of seconds in Retry-After.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/stats":{"get":{"operationId":"getTrackRecord","tags":["predictions"],"summary":"Track record","description":"Hit rates computed from every graded prediction: outcome (1X2), exact score and tipped goalscorers.","parameters":[{"name":"competition","in":"query","required":false,"description":"Competition key or slug from /api/v1/competitions (e.g. world-cup or world-cup-2026).","schema":{"type":"string","maxLength":60}}],"responses":{"200":{"description":"Track record","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackRecord"}}}},"400":{"description":"Unknown competition.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. Retry after the number of seconds in Retry-After.","headers":{"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"headers":{"RateLimit":{"description":"Remaining requests (r) and seconds until the window resets (t), e.g. \"default\";r=119;t=60.","schema":{"type":"string"}},"RateLimitPolicy":{"description":"Quota policy: \"default\";q=120;w=60 (requests per window in seconds, per IP).","schema":{"type":"string"}}},"schemas":{"Score":{"type":"object","required":["home","away"],"properties":{"home":{"type":"integer","minimum":0},"away":{"type":"integer","minimum":0}}},"TippedScorer":{"type":"object","required":["player","team"],"properties":{"player":{"type":"string"},"team":{"type":"string"}}},"SiteInfo":{"type":"object","required":["name","url","model","updatedAt","links"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"url":{"type":"string","format":"uri"},"model":{"type":"string","description":"Language model that writes the predictions."},"onlineSince":{"type":["string","null"],"format":"date"},"updatedAt":{"type":"string","format":"date"},"contact":{"type":"object","properties":{"email":{"type":"string","format":"email"},"developer":{"type":"string"},"developerUrl":{"type":"string","format":"uri"}}},"links":{"type":"object","additionalProperties":{"type":"string","format":"uri"}},"disclaimer":{"type":"string"}}},"Competition":{"type":"object","required":["key","slug","label","covered","matches"],"properties":{"key":{"type":"string"},"slug":{"type":"string"},"label":{"type":"string"},"covered":{"type":"boolean"},"matches":{"type":"integer","minimum":0},"url":{"type":["string","null"],"format":"uri"}}},"CompetitionList":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Competition"}}}},"PredictionSummary":{"type":"object","required":["id","competition","stage","kickoff","home","away","status","prediction","predictedScorers"],"properties":{"id":{"type":"string"},"url":{"type":["string","null"],"format":"uri"},"competition":{"type":"string"},"competitionKey":{"type":["string","null"]},"stage":{"type":"string"},"kickoff":{"type":"string","format":"date-time"},"venue":{"type":["string","null"]},"home":{"type":"string"},"away":{"type":"string"},"status":{"type":"string","enum":["pending","settled"]},"prediction":{"$ref":"#/components/schemas/Score"},"predictedScorers":{"type":"array","items":{"$ref":"#/components/schemas/TippedScorer"}},"result":{"oneOf":[{"$ref":"#/components/schemas/Score"},{"type":"null"}]},"grade":{"type":["string","null"],"enum":["exact","outcome","miss",null],"description":"exact = scoreline right, outcome = 1X2 right, miss = wrong. null until graded."},"updatedAt":{"type":["string","null"],"format":"date"}}},"Prediction":{"allOf":[{"$ref":"#/components/schemas/PredictionSummary"},{"type":"object","required":["reasoning","scorers","history"],"properties":{"reasoning":{"type":"string"},"scorers":{"type":"array","items":{"type":"object","properties":{"player":{"type":"string"},"team":{"type":"string"},"minute":{"type":["integer","null"]}}}},"history":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"prediction":{"$ref":"#/components/schemas/Score"},"scorers":{"type":"array","items":{"type":"string"}},"reason":{"type":"string"}}}}}}]},"PredictionList":{"type":"object","required":["total","limit","offset","items"],"properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PredictionSummary"}}}},"TrackRecord":{"type":"object","required":["predictions","graded","pending","outcome","exactScore","goalscorers"],"properties":{"competition":{"type":["string","null"]},"predictions":{"type":"integer"},"graded":{"type":"integer"},"pending":{"type":"integer"},"outcome":{"type":"object","properties":{"hits":{"type":"integer"},"ratePct":{"type":"number"}}},"exactScore":{"type":"object","properties":{"hits":{"type":"integer"},"ratePct":{"type":"number"}}},"goalscorers":{"type":"object","properties":{"hits":{"type":"integer"},"tipped":{"type":"integer"},"ratePct":{"type":"number"}}},"updatedAt":{"type":"string","format":"date"}}},"Problem":{"type":"object","description":"RFC 9457 problem details.","required":["type","title","status","detail","code"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"code":{"type":"string","enum":["bad_request","not_found","method_not_allowed","voting_closed","rate_limited","unavailable","error"]},"resolution":{"type":"string"},"instance":{"type":"string"}}}}}}