{"openapi":"3.1.0","info":{"title":"GDELT Pulse API","description":"Hybrid semantic + keyword search over GDELT news events.","version":"0.1.0"},"paths":{"/api/search":{"get":{"summary":"Search","description":"Hybrid semantic + keyword search over articles and clusters.","operationId":"search_api_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","description":"Search query text","title":"Q"},"description":"Search query text"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results","default":20,"title":"Limit"},"description":"Max results"},{"name":"semantic_weight","in":"query","required":false,"schema":{"type":"number","maximum":1.0,"minimum":0.0,"description":"Semantic vs keyword weight","default":0.5,"title":"Semantic Weight"},"description":"Semantic vs keyword weight"},{"name":"clusters","in":"query","required":false,"schema":{"type":"boolean","description":"Also search cluster centroids","default":false,"title":"Clusters"},"description":"Also search cluster centroids"},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by location (comma-separated)","title":"Location"},"description":"Filter by location (comma-separated)"},{"name":"person","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by person (comma-separated)","title":"Person"},"description":"Filter by person (comma-separated)"},{"name":"org","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by organization (comma-separated)","title":"Org"},"description":"Filter by organization (comma-separated)"},{"name":"theme","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by theme (comma-separated)","title":"Theme"},"description":"Filter by theme (comma-separated)"},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by domain (comma-separated). Matches the exact domain and any subdomain — e.g. 'corriere.it' matches both 'corriere.it' and 'video.corriere.it'.","title":"Domain"},"description":"Filter by domain (comma-separated). Matches the exact domain and any subdomain — e.g. 'corriere.it' matches both 'corriere.it' and 'video.corriere.it'."},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by source (comma-separated)","title":"Source"},"description":"Filter by source (comma-separated)"},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Start date (ISO format)","title":"Date From"},"description":"Start date (ISO format)"},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"End date (ISO format)","title":"Date To"},"description":"End date (ISO format)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clusters":{"get":{"summary":"List Clusters","description":"List active clusters, optionally filtered by article metadata.","operationId":"list_clusters_api_clusters_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max clusters to return","default":100,"title":"Limit"},"description":"Max clusters to return"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","description":"Sort: recent, articles, oldest","default":"recent","title":"Sort"},"description":"Sort: recent, articles, oldest"},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by location (comma-separated)","title":"Location"},"description":"Filter by location (comma-separated)"},{"name":"person","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by person (comma-separated)","title":"Person"},"description":"Filter by person (comma-separated)"},{"name":"org","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by organization (comma-separated)","title":"Org"},"description":"Filter by organization (comma-separated)"},{"name":"theme","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by theme (comma-separated)","title":"Theme"},"description":"Filter by theme (comma-separated)"},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by source domain (comma-separated). Soft match: 'corriere.it' also matches 'video.corriere.it'.","title":"Domain"},"description":"Filter by source domain (comma-separated). Soft match: 'corriere.it' also matches 'video.corriere.it'."},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by canonical source slug (comma-separated)","title":"Source"},"description":"Filter by canonical source slug (comma-separated)"},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Start date (ISO format)","title":"Date From"},"description":"Start date (ISO format)"},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"End date (ISO format)","title":"Date To"},"description":"End date (ISO format)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Clusters Api Clusters Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clusters/{cluster_id}":{"get":{"summary":"Get Cluster Detail","description":"Get a single cluster and its member articles.","operationId":"get_cluster_detail_api_clusters__cluster_id__get","parameters":[{"name":"cluster_id","in":"path","required":true,"schema":{"type":"string","title":"Cluster Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterDetailOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/articles":{"get":{"summary":"List Articles","description":"List recent articles, newest first, with optional filters.","operationId":"list_articles_api_articles_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max articles to return","default":50,"title":"Limit"},"description":"Max articles to return"},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by location (comma-separated)","title":"Location"},"description":"Filter by location (comma-separated)"},{"name":"person","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by person (comma-separated)","title":"Person"},"description":"Filter by person (comma-separated)"},{"name":"org","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by organization (comma-separated)","title":"Org"},"description":"Filter by organization (comma-separated)"},{"name":"theme","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by GDELT theme (comma-separated)","title":"Theme"},"description":"Filter by GDELT theme (comma-separated)"},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by source domain (comma-separated). Soft match: 'corriere.it' also matches 'video.corriere.it'.","title":"Domain"},"description":"Filter by source domain (comma-separated). Soft match: 'corriere.it' also matches 'video.corriere.it'."},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by canonical source slug (comma-separated)","title":"Source"},"description":"Filter by canonical source slug (comma-separated)"},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Start date (ISO)","title":"Date From"},"description":"Start date (ISO)"},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"End date (ISO)","title":"Date To"},"description":"End date (ISO)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Articles Api Articles Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/stats":{"get":{"summary":"Get Stats","description":"Dashboard statistics.","operationId":"get_stats_api_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/keys":{"get":{"tags":["auth"],"summary":"Get Key","operationId":"get_key_api_auth_keys_get","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyMeta"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["auth"],"summary":"Create Key","operationId":"create_key_api_auth_keys_post","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string","title":"Authorization"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyCreated"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["auth"],"summary":"Revoke Key","operationId":"revoke_key_api_auth_keys_delete","parameters":[{"name":"authorization","in":"header","required":true,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Key Api Auth Keys Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health":{"get":{"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"head":{"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"ClusterDetailOut":{"properties":{"cluster":{"additionalProperties":true,"type":"object","title":"Cluster"},"articles":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Articles"}},"type":"object","required":["cluster","articles"],"title":"ClusterDetailOut"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KeyCreated":{"properties":{"key":{"type":"string","title":"Key"},"prefix":{"type":"string","title":"Prefix"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["key","prefix","created_at"],"title":"KeyCreated"},"KeyMeta":{"properties":{"active":{"type":"boolean","title":"Active"},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["active"],"title":"KeyMeta"},"ScoredArticleOut":{"properties":{"article":{"additionalProperties":true,"type":"object","title":"Article"},"semantic_rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Semantic Rank"},"keyword_rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Keyword Rank"},"rrf_score":{"type":"number","title":"Rrf Score","default":0.0}},"type":"object","required":["article"],"title":"ScoredArticleOut"},"ScoredClusterOut":{"properties":{"cluster":{"additionalProperties":true,"type":"object","title":"Cluster"},"cosine_distance":{"type":"number","title":"Cosine Distance"},"rank":{"type":"integer","title":"Rank"}},"type":"object","required":["cluster","cosine_distance","rank"],"title":"ScoredClusterOut"},"SearchResponse":{"properties":{"query":{"type":"string","title":"Query"},"total_semantic_hits":{"type":"integer","title":"Total Semantic Hits","default":0},"total_keyword_hits":{"type":"integer","title":"Total Keyword Hits","default":0},"articles":{"items":{"$ref":"#/components/schemas/ScoredArticleOut"},"type":"array","title":"Articles","default":[]},"clusters":{"items":{"$ref":"#/components/schemas/ScoredClusterOut"},"type":"array","title":"Clusters","default":[]}},"type":"object","required":["query"],"title":"SearchResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}