{"openapi":"3.1.0","info":{"title":"Plost Read API","version":"1.1.0","description":"Read-only aggregates and cached website checks. Use an account API key with explicit website selection, or a legacy single-site sharing credential. Keys and current account/website access are rechecked for every request. Never publish credentials or put them in URLs."},"servers":[{"url":"/"}],"components":{"securitySchemes":{"accountApiKey":{"type":"http","scheme":"bearer","description":"Create under Account settings → API keys. Complete credential is shown only once. Scope is an explicit list of selected websites, intersected with current account access."},"sharingCredential":{"type":"http","scheme":"bearer","description":"Create a named share under Site settings → Visibility; use the final /share/ path segment. Scope: that single site's aggregated report."}}},"paths":{"/api/v1/sites":{"get":{"operationId":"getAuthorizedWebsites","summary":"List websites currently authorized by this API key","security":[{"accountApiKey":[]},{"sharingCredential":[]}],"description":"Create an account-level read-only API key in Account settings → API keys and explicitly choose 1–100 websites. The complete plost_ credential is shown once and stored only as a SHA-256 hash. This endpoint rechecks key revocation, owner suspension and current team/guest access on every request; changed scope is effective for the next request. An active key with no remaining authorized websites returns an empty array. Legacy sharing credentials return their single website. Never put credentials in a URL. 60 requests/minute/credential/process.","responses":{"200":{"description":"Current authorized websites. No account details, credentials or unselected websites.","content":{"application/json":{"schema":{"type":"object","required":["apiVersion","generatedAt","sites"],"properties":{"apiVersion":{"type":"string","const":"1"},"generatedAt":{"type":"string","format":"date-time"},"sites":{"type":"array","maxItems":100,"items":{"type":"object","required":["id","domain","name","timezone"],"properties":{"id":{"type":"string","pattern":"^[1-9][0-9]*$"},"domain":{"type":"string"},"name":{"type":["string","null"]},"timezone":{"type":"string"}}}}}}}}},"400":{"description":"Invalid or repeated query parameter","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"401":{"description":"Missing, invalid or revoked credential","headers":{"WWW-Authenticate":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Credential domain mismatch","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"429":{"description":"Rate limit exceeded; honor Retry-After","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Data service unavailable; do not interpret as zero traffic","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/v1/report":{"get":{"operationId":"getSiteReport","summary":"Range totals, traffic trend, top pages and sources, certificate/domain expiry and HTTP availability","security":[{"accountApiKey":[]},{"sharingCredential":[]}],"description":"Uses an account API key or legacy sharing credential, as /stats does. Use /sites to discover the current explicit scope; domain is required when multiple websites are authorized. Every request rechecks scope, current account membership, suspension and revocation. range=today/yesterday follows the site's calendar days; 7d/30d includes today; month covers the current calendar month. 24h is exactly 24 rolling hours. The previous period is the previous full day, previous 7/30 days, previous calendar month, or preceding 24 hours. Today/month comparisons are incomplete current periods against full previous periods. Totals count distinct visitors across the entire range, never sums of daily UV. details=1 adds time-ordered buckets (future values are null), and the top five pages and referrer sources by distinct visitors, matching the web report. HTTP state older than 10 minutes is unknown. Expiry dates are cached server check results; stale dates are retained and marked. This API performs no probes and exposes no target paths, IPs, registrars or diagnostic errors. Health failure does not invalidate traffic. 60 requests/minute/credential/process; honor Retry-After.","parameters":[{"name":"range","in":"query","schema":{"type":"string","enum":["today","yesterday","24h","7d","30d","month"],"default":"today"}},{"name":"details","in":"query","schema":{"type":"string","enum":["0","1"],"default":"0"}},{"name":"domain","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Select an authorized stored domain. Required for an account key with multiple websites; an unselected or inaccessible site returns 403."}],"responses":{"200":{"description":"Scoped aggregate report. details or health may be null; unavailable data must not be treated as zero.","content":{"application/json":{"schema":{"type":"object","required":["apiVersion","site","generatedAt","range","data","details","health","healthError"],"properties":{"apiVersion":{"type":"string","const":"1"},"site":{"type":"object","required":["domain","timezone"],"properties":{"domain":{"type":"string"},"timezone":{"type":"string"}}},"generatedAt":{"type":"string","format":"date-time"},"range":{"type":"object","required":["key","start","end","interval"],"properties":{"key":{"type":"string","enum":["today","yesterday","24h","7d","30d","month"]},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"interval":{"type":"string","enum":["hour","day"]}}},"data":{"type":"object","required":["online","totals","previous"],"properties":{"online":{"type":"integer","minimum":0,"maximum":9007199254740991},"totals":{"type":"object","required":["visitors","visits","pageviews"],"properties":{"visitors":{"type":"integer","minimum":0,"maximum":9007199254740991},"visits":{"type":"integer","minimum":0,"maximum":9007199254740991},"pageviews":{"type":"integer","minimum":0,"maximum":9007199254740991}}},"previous":{"type":"object","required":["visitors","visits","pageviews"],"properties":{"visitors":{"type":"integer","minimum":0,"maximum":9007199254740991},"visits":{"type":"integer","minimum":0,"maximum":9007199254740991},"pageviews":{"type":"integer","minimum":0,"maximum":9007199254740991}}}}},"details":{"anyOf":[{"type":"null"},{"type":"object","required":["series","pages","sources"],"properties":{"series":{"type":"array","maxItems":1000,"items":{"type":"object","required":["timestamp","visitors","pageviews"],"properties":{"timestamp":{"type":"string","format":"date-time"},"visitors":{"type":["integer","null"],"minimum":0,"maximum":9007199254740991},"pageviews":{"type":["integer","null"],"minimum":0,"maximum":9007199254740991}}}},"pages":{"type":"array","maxItems":5,"items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string"},"value":{"type":"integer","minimum":0,"maximum":9007199254740991}}}},"sources":{"type":"array","maxItems":5,"items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string"},"value":{"type":"integer","minimum":0,"maximum":9007199254740991}}}}}}]},"health":{"anyOf":[{"type":"null"},{"type":"object","required":["enabled","checkedAt","status","statusCode","durationMs","certificate","domain"],"properties":{"enabled":{"type":"boolean"},"checkedAt":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["up","down","unknown","paused"]},"statusCode":{"type":["integer","null"]},"durationMs":{"type":["integer","null"],"minimum":0},"certificate":{"type":"object","required":["expiresAt","checkedAt","stale","error"],"properties":{"expiresAt":{"type":["string","null"],"format":"date-time"},"checkedAt":{"type":["string","null"],"format":"date-time"},"stale":{"type":"boolean"},"error":{"type":["string","null"]}}},"domain":{"type":"object","required":["expiresAt","checkedAt","stale","error","domain"],"properties":{"expiresAt":{"type":["string","null"],"format":"date-time"},"checkedAt":{"type":["string","null"],"format":"date-time"},"stale":{"type":"boolean"},"error":{"type":["string","null"]},"domain":{"type":["string","null"]}}}}}]},"healthError":{"type":["string","null"]}}}}}},"400":{"description":"Invalid or repeated query parameter","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"401":{"description":"Missing, invalid or revoked credential","headers":{"WWW-Authenticate":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Credential domain mismatch","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"429":{"description":"Rate limit exceeded; honor Retry-After","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Data service unavailable; do not interpret as zero traffic","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/v1/stats":{"get":{"operationId":"getSiteStatistics","summary":"Online visitors, today and yesterday","security":[{"accountApiKey":[]},{"sharingCredential":[]}],"description":"Days use the stored site timezone (invalid historical zones fall back to UTC), including daylight saving. Online means distinct visitors with an event in the last 300 seconds, not persistent connections. visitors counts distinct visitor IDs, visits distinct session IDs, pageviews only pageview events. Current browser IDs are persistent; migrated and legacy IDs may use different schemes. Today is incomplete. Writes are buffered; recent events may appear after a delay. Single-process rate limit: 60 requests/minute/credential; polling every 30–60 seconds is recommended. No cross-origin browser credential support. Never sum daily visitors as unique visitors across days.","parameters":[{"name":"domain","in":"query","required":false,"description":"Select an authorized stored domain. Required when an account API key authorizes multiple websites. Unselected or no-longer-accessible websites return 403.","schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"period","in":"query","required":false,"schema":{"type":"string","enum":["summary","live","today","yesterday"],"default":"summary"}}],"responses":{"200":{"description":"Successful aggregate response. No visitor-level data.","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"type":"object","required":["apiVersion","site","period","generatedAt","onlineWindowSeconds","data"],"properties":{"apiVersion":{"type":"string","const":"1"},"site":{"type":"object","required":["domain","timezone"],"properties":{"domain":{"type":"string"},"timezone":{"type":"string"}}},"period":{"type":"string","enum":["summary","live","today","yesterday"]},"generatedAt":{"type":"string","format":"date-time"},"onlineWindowSeconds":{"type":"integer","const":300},"data":{"anyOf":[{"type":"object","required":["online","today","yesterday"],"properties":{"online":{"type":"integer","minimum":0},"today":{"type":"object","required":["date","visitors","visits","pageviews"],"properties":{"date":{"type":"string","format":"date"},"visitors":{"type":"integer","minimum":0},"visits":{"type":"integer","minimum":0},"pageviews":{"type":"integer","minimum":0}}},"yesterday":{"type":"object","required":["date","visitors","visits","pageviews"],"properties":{"date":{"type":"string","format":"date"},"visitors":{"type":"integer","minimum":0},"visits":{"type":"integer","minimum":0},"pageviews":{"type":"integer","minimum":0}}}}},{"type":"object","required":["online"],"properties":{"online":{"type":"integer","minimum":0}}},{"type":"object","required":["date","visitors","visits","pageviews"],"properties":{"date":{"type":"string","format":"date"},"visitors":{"type":"integer","minimum":0},"visits":{"type":"integer","minimum":0},"pageviews":{"type":"integer","minimum":0}}}]}}}}}},"400":{"description":"Invalid or repeated query parameter","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"401":{"description":"Missing, invalid or revoked credential","headers":{"WWW-Authenticate":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Credential domain mismatch","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"429":{"description":"Rate limit exceeded; honor Retry-After","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Data service unavailable; do not interpret as zero traffic","headers":{},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}}}