{
  "openapi": "3.1.0",
  "info": {
    "title": "NewsData.io API",
    "version": "1.0.0",
    "summary": "JSON API for newsdata.io endpoints (latest, archive, crypto, market, sources, count, topics, logs).",
    "description": "Read-only REST API. Each call is rate-limited per IP and per user-tier, and charged against a per-user API credit balance. All endpoints are read-only `GET`. Responses are JSON with `Content-Type: application/json; charset=utf-8`.\n\n**Authentication.** Provide your API key either as the `apikey` query string parameter or as the `X-ACCESS-KEY` HTTP header. Both are accepted; the header takes precedence when both are present.\n\n**Webhook flag.** When `webhook=1` is sent on any endpoint, every non-200 response is converted to HTTP `202 Accepted` so that webhook delivery systems do not treat client/server errors as terminal. The JSON body is unchanged.\n\n**Pagination.** For `sort=pubdateasc|pubdatedesc` the `page` cursor is the 19-character `timestamp` of the last article in the previous page (cursor-based via Elasticsearch `search_after`). For `sort=relevancy|source|fetched_at` the cursor is the 1-based page number (max page = `10000 / size`).\n\n**Quota & rate limits.** Two response headers are returned on every successful call: `X-RateLimit-Remaining` (sliding 15-minute request budget) and `X-API-Limit-Remaining` (daily/credit budget). Each header is duplicated in lowercase form (`x_rate_limit_remaining`, `x_api_limit_remaining`) for legacy clients.\n\n**Filter compatibility.** Several filters are mutually exclusive: at most one of `{q, qInTitle, qInMeta}`; at most one of `{domain, domainurl, excludedomain}`; `country` and `excludecountry`; `category` and `excludecategory`; `language` and `excludelanguage`; `timeframe` and `{from_date, to_date}`. The `id` and `url` filters are exclusive with every other filter.",
    "termsOfService": "https://newsdata.io/terms-and-conditions",
    "contact": {
      "name": "NewsData.io support",
      "url": "https://newsdata.io/contact",
      "email": "support@newsdata.io"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://newsdata.io/terms-and-conditions"
    }
  },
  "externalDocs": {
    "description": "Official documentation",
    "url": "https://newsdata.io/documentation"
  },
  "servers": [
    {
      "url": "https://newsdata.io/api",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "News",    "description": "Latest and archive news articles." },
    { "name": "Crypto",  "description": "Cryptocurrency news (separate index)." },
    { "name": "Market",  "description": "Financial-market news, requires the `is_financial` plan flag." },
    { "name": "Sources", "description": "News source directory." },
    { "name": "Count",   "description": "Article counts and time-bucketed histograms." },
    { "name": "Topics",  "description": "Curated topic collections (clustered articles)." },
    { "name": "Logs",    "description": "Per-user access logs and runtime API log. Admin-only via `access_key`." },
    { "name": "Meta",    "description": "Service metadata and health." }
  ],
  "security": [
    { "ApiKeyQuery": [] },
    { "ApiKeyHeader": [] }
  ],
  "paths": {
    "/1/": {
      "get": {
        "tags": ["Meta"],
        "operationId": "getHello",
        "summary": "Liveness probe",
        "description": "Returns a fixed plaintext greeting. Useful as a smoke test. No authentication. Subject to the IP-based DDoS limiter.",
        "security": [],
        "responses": {
          "200": {
            "description": "Service alive.",
            "content": {
              "text/html": {
                "schema": { "type": "string" },
                "examples": { "default": { "value": "Hello, NewsData Readers!" } }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/latest": {
      "get": {
        "tags": ["News"],
        "operationId": "getLatest",
        "summary": "Latest news articles",
        "description": "Returns the most recently fetched news articles. Free-tier callers see articles delayed by 12 hours (free `to_date` is clamped to `now - 12h`). Paid callers see up to `now`. Default window is the past 48 hours.\n\n`/1/news` is a historical alias of this endpoint and behaves identically.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/ExcludeCountry" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/ExcludeCategory" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/Id" },
          { "$ref": "#/components/parameters/Url" },
          { "$ref": "#/components/parameters/Timeframe" },
          { "$ref": "#/components/parameters/Timezone" },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/SentimentScore" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Region" },
          { "$ref": "#/components/parameters/Organization" },
          { "$ref": "#/components/parameters/Creator" },
          { "$ref": "#/components/parameters/Datatype" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Sort" },
          { "$ref": "#/components/parameters/Adv" },
          { "$ref": "#/components/parameters/ExcludeField" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "X-API-Limit-Remaining":  { "$ref": "#/components/headers/XApiLimitRemaining" },
              "x_api_limit_remaining":  { "$ref": "#/components/headers/XApiLimitRemaining" },
              "X-RateLimit-Remaining":  { "$ref": "#/components/headers/XRateLimitRemaining" },
              "x_rate_limit_remaining": { "$ref": "#/components/headers/XRateLimitRemaining" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleListEnvelope" },
                "examples": { "default": { "$ref": "#/components/examples/ArticleListExample" } }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },

    "/1/news": {
      "get": {
        "tags": ["News"],
        "operationId": "getNewsAlias",
        "summary": "Latest news articles (alias of /1/latest)",
        "deprecated": true,
        "description": "Historical alias for `/1/latest`. Parameters and responses are identical. New integrations should use `/1/latest`.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleListEnvelope" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/archive": {
      "get": {
        "tags": ["News"],
        "operationId": "getArchive",
        "summary": "Historical news archive",
        "description": "Returns historical news articles. Requires the `is_archive` plan flag (HTTP 403 otherwise). Each call to this endpoint consumes 5 API credits (vs 1 for `/latest`). At least one of the following filters is required (non-admin callers): `category, country, domain, domainurl, full_content, id, url, image, language, prioritydomain, q, qInTitle, qInMeta, video`.\n\nCallers may go back up to the user's `timeperiod` days (admin: unlimited). Subject to a stricter DDoS limit (`2 req/s` per IP) and rate limit (`450 req / 15 min` for paid).",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/ExcludeCountry" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/ExcludeCategory" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/Id" },
          { "$ref": "#/components/parameters/Url" },
          { "$ref": "#/components/parameters/FromDate" },
          { "$ref": "#/components/parameters/ToDate" },
          { "$ref": "#/components/parameters/Timezone" },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/SentimentScore" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Region" },
          { "$ref": "#/components/parameters/Organization" },
          { "$ref": "#/components/parameters/Creator" },
          { "$ref": "#/components/parameters/Datatype" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Sort" },
          { "$ref": "#/components/parameters/Adv" },
          { "$ref": "#/components/parameters/ExcludeField" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "headers": {
              "X-API-Limit-Remaining":  { "$ref": "#/components/headers/XApiLimitRemaining" },
              "X-RateLimit-Remaining":  { "$ref": "#/components/headers/XRateLimitRemaining" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ArticleListEnvelope" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },

    "/1/crypto": {
      "get": {
        "tags": ["Crypto"],
        "operationId": "getCrypto",
        "summary": "Cryptocurrency news",
        "description": "Returns articles from the cryptocurrency news index. Requires the `is_crypto` plan flag (HTTP 401 otherwise). Free-tier callers receive a 7-day window ending at `now - 12h` and may not pass `from_date`, `to_date`, or `timeframe`.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Coin" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/Id" },
          { "$ref": "#/components/parameters/Url" },
          { "$ref": "#/components/parameters/FromDate" },
          { "$ref": "#/components/parameters/ToDate" },
          { "$ref": "#/components/parameters/Timeframe" },
          { "$ref": "#/components/parameters/Timezone" },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Sort" },
          { "$ref": "#/components/parameters/Adv" },
          { "$ref": "#/components/parameters/ExcludeField" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CryptoArticleListEnvelope" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },

    "/1/market": {
      "get": {
        "tags": ["Market"],
        "operationId": "getMarket",
        "summary": "Financial-market news",
        "description": "Returns news articles annotated with one or more financial tickers (`symbol`). Requires the `is_financial` plan flag (HTTP 403 otherwise). Free-tier callers are clamped to a 7-day window ending at `now - 12h` and may not pass `from_date`, `to_date`, or `timeframe`. Internally routes between the latest index and the archive index depending on whether the caller's window overlaps `now`.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Symbol" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/ExcludeCountry" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/Id" },
          { "$ref": "#/components/parameters/Url" },
          { "$ref": "#/components/parameters/FromDate" },
          { "$ref": "#/components/parameters/ToDate" },
          { "$ref": "#/components/parameters/Timeframe" },
          { "$ref": "#/components/parameters/Timezone" },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/SentimentScore" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Organization" },
          { "$ref": "#/components/parameters/Creator" },
          { "$ref": "#/components/parameters/Datatype" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Sort" },
          { "$ref": "#/components/parameters/Adv" },
          { "$ref": "#/components/parameters/ExcludeField" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MarketArticleListEnvelope" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/sources": {
      "get": {
        "tags": ["Sources"],
        "operationId": "getSources",
        "summary": "List news sources",
        "description": "Returns the catalogue of news sources available to the caller, filtered by `country`, `category`, `language`, `domainurl`, and `prioritydomain`. Up to 100 sources are returned per call. Cached on the server side for 15 minutes per filter combination.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SourceListEnvelope" },
                "examples": { "default": { "$ref": "#/components/examples/SourceListExample" } }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/count": {
      "get": {
        "tags": ["Count"],
        "operationId": "getCount",
        "summary": "Article count, with optional time-bucketing",
        "description": "Returns either the total number of articles matching the filters (`interval=all`, default) or a time-bucketed histogram (`interval=hour|day`). The two response shapes are different — see the `oneOf` on the response schema. Requires the `is_count` plan flag (HTTP 403 otherwise). Each call consumes 5 API credits. `from_date` and `to_date` are mandatory.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/ExcludeCountry" },
          { "$ref": "#/components/parameters/Category" },
          { "$ref": "#/components/parameters/ExcludeCategory" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/FromDate", "required": true },
          { "$ref": "#/components/parameters/ToDate",   "required": true },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/SentimentScore" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Region" },
          { "$ref": "#/components/parameters/Organization" },
          { "$ref": "#/components/parameters/Creator" },
          { "$ref": "#/components/parameters/Datatype" },
          { "$ref": "#/components/parameters/Interval" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/CountSort" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success. Body shape depends on the `interval` parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/CountSummaryEnvelope" },
                    { "$ref": "#/components/schemas/CountBucketEnvelope" }
                  ]
                },
                "examples": {
                  "intervalAll":  { "$ref": "#/components/examples/CountSummaryExample" },
                  "intervalHour": { "$ref": "#/components/examples/CountBucketExample" }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/crypto/count": {
      "get": {
        "tags": ["Count"],
        "operationId": "getCryptoCount",
        "summary": "Crypto article count",
        "description": "Same shape as `/1/count` but scoped to the crypto index. Filter set is a subset of `/1/count` (no `region`, no `organization`, no `tag`/`sentiment` floors).",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/QInTitle" },
          { "$ref": "#/components/parameters/QInMeta" },
          { "$ref": "#/components/parameters/Coin" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/FromDate", "required": true },
          { "$ref": "#/components/parameters/ToDate",   "required": true },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Interval" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/CountSort" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/CountSummaryEnvelope" },
                    { "$ref": "#/components/schemas/CountBucketEnvelope" }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/market/count": {
      "get": {
        "tags": ["Count"],
        "operationId": "getMarketCount",
        "summary": "Market article count",
        "description": "Same shape as `/1/count` but scoped to the financial-market index. Adds `symbol` filter.",
        "parameters": [
          { "$ref": "#/components/parameters/ApiKey" },
          { "$ref": "#/components/parameters/Q" },
          { "$ref": "#/components/parameters/Symbol" },
          { "$ref": "#/components/parameters/Country" },
          { "$ref": "#/components/parameters/ExcludeCountry" },
          { "$ref": "#/components/parameters/Language" },
          { "$ref": "#/components/parameters/ExcludeLanguage" },
          { "$ref": "#/components/parameters/Domain" },
          { "$ref": "#/components/parameters/DomainUrl" },
          { "$ref": "#/components/parameters/ExcludeDomain" },
          { "$ref": "#/components/parameters/PriorityDomain" },
          { "$ref": "#/components/parameters/FromDate", "required": true },
          { "$ref": "#/components/parameters/ToDate",   "required": true },
          { "$ref": "#/components/parameters/Image" },
          { "$ref": "#/components/parameters/Video" },
          { "$ref": "#/components/parameters/FullContent" },
          { "$ref": "#/components/parameters/RemoveDuplicate" },
          { "$ref": "#/components/parameters/Sentiment" },
          { "$ref": "#/components/parameters/SentimentScore" },
          { "$ref": "#/components/parameters/Tag" },
          { "$ref": "#/components/parameters/Organization" },
          { "$ref": "#/components/parameters/Creator" },
          { "$ref": "#/components/parameters/Datatype" },
          { "$ref": "#/components/parameters/Interval" },
          { "$ref": "#/components/parameters/Size" },
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/CountSort" },
          { "$ref": "#/components/parameters/Webhook" }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/CountSummaryEnvelope" },
                    { "$ref": "#/components/schemas/CountBucketEnvelope" }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/1/topic/all": {
      "get": {
        "tags": ["Topics"],
        "operationId": "getAllTopics",
        "summary": "List curated topics",
        "description": "Returns the catalogue of curated topic clusters available to the public.",
        "security": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["status", "result"],
                  "properties": {
                    "status": { "type": "string", "const": "success" },
                    "result": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Topic" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/1/topic/view": {
      "get": {
        "tags": ["Topics"],
        "operationId": "getTopicView",
        "summary": "Articles in a curated topic",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "Numeric topic id from `/1/topic/all`.",
            "schema": { "type": "integer", "minimum": 1 },
            "examples": { "default": { "value": 12 } }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number.",
            "schema": { "type": "integer", "minimum": 1 }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["status", "results"],
                  "properties": {
                    "status":  { "type": "string", "const": "success" },
                    "results": {
                      "type": "array",
                      "items": { "type": "object", "additionalProperties": true }
                    }
                  }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },

    "/1/log/user": {
      "get": {
        "tags": ["Logs"],
        "operationId": "getUserAccessLog",
        "summary": "Per-API-key access statistics (admin)",
        "description": "Returns the last 30 days of access counts and a tail of the most recent log entries for one API key. Authenticated by `access_key` (server-side `LOG_VIEW_SECRET`), not by user `apikey`.",
        "security": [{ "LogAccessKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/LogAccessKey" },
          {
            "name": "apikey",
            "in": "query",
            "required": true,
            "description": "The user's API key whose log is being requested.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UserAccessLogEnvelope" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },

    "/1/log/user/download": {
      "get": {
        "tags": ["Logs"],
        "operationId": "downloadUserAccessLog",
        "summary": "Download access log as ZIP (admin)",
        "security": [{ "LogAccessKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/LogAccessKey" },
          {
            "name": "apikey",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "ZIP file containing per-day access logs.",
            "headers": {
              "Content-Disposition": {
                "schema": { "type": "string" },
                "description": "attachment; filename=user_logs_YYYYMMDD.zip"
              }
            },
            "content": {
              "application/zip": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },

    "/1/log/api": {
      "get": {
        "tags": ["Logs"],
        "operationId": "getApiLog",
        "summary": "Tail of the server-side API log (admin)",
        "security": [{ "LogAccessKey": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/LogAccessKey" },
          {
            "name": "size",
            "in": "query",
            "required": false,
            "description": "Number of log lines to tail (1–2000, default 500).",
            "schema": { "type": "integer", "minimum": 1, "maximum": 2000, "default": 500 }
          }
        ],
        "responses": {
          "200": {
            "description": "Tail of NewsData.log, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["status", "result"],
                  "properties": {
                    "status": { "type": "string", "const": "success" },
                    "result": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  },

  "components": {
    "securitySchemes": {
      "ApiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "apikey",
        "description": "API key passed as a query string parameter."
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-ACCESS-KEY",
        "description": "API key passed as an HTTP header. Takes precedence over `apikey` query param when both are present."
      },
      "LogAccessKey": {
        "type": "apiKey",
        "in": "query",
        "name": "access_key",
        "description": "Server-side admin secret (`LOG_VIEW_SECRET`). Required only on `/1/log/**` endpoints."
      }
    },

    "parameters": {
      "ApiKey": {
        "name": "apikey",
        "in": "query",
        "required": false,
        "description": "User API key. Alternatively, send the `X-ACCESS-KEY` HTTP header. One of the two MUST be present.",
        "schema": { "type": "string", "minLength": 8, "maxLength": 128 },
        "examples": { "default": { "value": "pub_live_a1b2c3d4e5f6" } }
      },
      "Q": {
        "name": "q",
        "in": "query",
        "required": false,
        "description": "Free-text query, matched against `title`, `link`, `full_description`, `description`, `content`, `keywords` with AND-default operator. Maximum length is governed by the user's `q_limit` (default 100). Reserved Elasticsearch characters (`+ - = & | > < ! { } [ ] ^ ~ * ? : \\ /`) are auto-escaped. Mutually exclusive with `qInTitle` and `qInMeta`.",
        "schema": { "type": "string", "maxLength": 512 },
        "examples": {
          "default":      { "value": "elections" },
          "phrase":       { "value": "\"new york\"" },
          "withOperator": { "value": "elections AND results" }
        }
      },
      "QInTitle": {
        "name": "qInTitle",
        "in": "query",
        "required": false,
        "description": "Free-text query, matched against `title` only. Mutually exclusive with `q` and `qInMeta`.",
        "schema": { "type": "string", "maxLength": 512 }
      },
      "QInMeta": {
        "name": "qInMeta",
        "in": "query",
        "required": false,
        "description": "Free-text query, matched against `title`, `link`, `description`, and `keywords`. Mutually exclusive with `q` and `qInTitle`.",
        "schema": { "type": "string", "maxLength": 512 }
      },
      "Country": {
        "name": "country",
        "in": "query",
        "required": false,
        "description": "Comma-separated ISO 3166-1 alpha-2 country codes. Max `filter_limit` values (default 5). Mutually exclusive with `excludecountry`.",
        "explode": false,
        "style": "form",
        "schema": {
          "type": "array",
          "items": { "type": "string", "minLength": 2, "maxLength": 2 },
          "maxItems": 5
        },
        "examples": {
          "single":   { "value": "us" },
          "multiple": { "value": "us,gb,in" }
        }
      },
      "ExcludeCountry": {
        "name": "excludecountry",
        "in": "query",
        "required": false,
        "description": "Comma-separated ISO country codes to exclude. Mutually exclusive with `country`.",
        "explode": false,
        "style": "form",
        "schema": {
          "type": "array",
          "items": { "type": "string", "minLength": 2, "maxLength": 2 },
          "maxItems": 5
        }
      },
      "Category": {
        "name": "category",
        "in": "query",
        "required": false,
        "description": "Comma-separated category names. Mutually exclusive with `excludecategory`.",
        "explode": false,
        "style": "form",
        "schema": {
          "type": "array",
          "items": { "type": "string" },
          "maxItems": 5
        },
        "examples": {
          "default": { "value": "business,technology" }
        }
      },
      "ExcludeCategory": {
        "name": "excludecategory",
        "in": "query",
        "required": false,
        "description": "Comma-separated category names to exclude. Mutually exclusive with `category`.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Language": {
        "name": "language",
        "in": "query",
        "required": false,
        "description": "Comma-separated ISO 639-1 language codes. Mutually exclusive with `excludelanguage`.",
        "explode": false,
        "style": "form",
        "schema": {
          "type": "array",
          "items": { "type": "string", "minLength": 2, "maxLength": 3 },
          "maxItems": 5
        },
        "examples": { "default": { "value": "en,hi" } }
      },
      "ExcludeLanguage": {
        "name": "excludelanguage",
        "in": "query",
        "required": false,
        "description": "Comma-separated language codes to exclude. Mutually exclusive with `language`.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Domain": {
        "name": "domain",
        "in": "query",
        "required": false,
        "description": "Comma-separated source identifiers (canonical `name` values from `/1/sources`). Mutually exclusive with `domainurl` and `excludedomain`.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 },
        "examples": { "default": { "value": "bbc,cnn,reuters" } }
      },
      "DomainUrl": {
        "name": "domainurl",
        "in": "query",
        "required": false,
        "description": "Comma-separated source domain URLs (e.g. `bbc.com`). Mutually exclusive with `domain` and `excludedomain`.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "ExcludeDomain": {
        "name": "excludedomain",
        "in": "query",
        "required": false,
        "description": "Comma-separated source URLs to exclude. Mutually exclusive with `domain` and `domainurl`.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "PriorityDomain": {
        "name": "prioritydomain",
        "in": "query",
        "required": false,
        "description": "Restrict to a source-priority tier. `top` = priorities 0–13000, `medium` = 0–200000, `low` = 0–600000.",
        "schema": {
          "type": "string",
          "enum": ["top", "medium", "low"]
        }
      },
      "Id": {
        "name": "id",
        "in": "query",
        "required": false,
        "description": "Comma-separated `article_id` values (32-character hex). Mutually exclusive with EVERY other filter except `apikey`. Maximum number of IDs equals the caller's `max_response_limit` (default 50 paid, 10 free).",
        "explode": false,
        "style": "form",
        "schema": {
          "type": "array",
          "items": { "type": "string", "minLength": 32, "maxLength": 32 },
          "maxItems": 50
        }
      },
      "Url": {
        "name": "url",
        "in": "query",
        "required": false,
        "description": "Filter to a specific article URL. Tracking parameters (utm_*, fbclid, gclid, etc.) are stripped server-side. Mutually exclusive with EVERY other filter except `apikey`. Max length 512.",
        "schema": { "type": "string", "format": "uri", "maxLength": 512 }
      },
      "FromDate": {
        "name": "from_date",
        "in": "query",
        "required": false,
        "description": "Lower bound on `pubDate`. ISO 8601 in UTC (`YYYY-MM-DD HH:MM:SS`). Cannot be in the future. Non-admin callers cannot go back further than their plan `timeperiod`.",
        "schema": { "type": "string", "format": "date-time" },
        "examples": { "default": { "value": "2026-05-01 00:00:00" } }
      },
      "ToDate": {
        "name": "to_date",
        "in": "query",
        "required": false,
        "description": "Upper bound on `pubDate`. ISO 8601 in UTC. Cannot be in the future.",
        "schema": { "type": "string", "format": "date-time" }
      },
      "Timeframe": {
        "name": "timeframe",
        "in": "query",
        "required": false,
        "description": "Relative window ending at `now`. Integer = hours (1–48). Integer suffixed with `m` = minutes (1–2880). Mutually exclusive with `from_date` and `to_date`. Not available to free callers.",
        "schema": {
          "type": "string",
          "pattern": "^[1-9][0-9]*m?$"
        },
        "examples": {
          "hours":   { "value": "24" },
          "minutes": { "value": "15m" }
        }
      },
      "Timezone": {
        "name": "timezone",
        "in": "query",
        "required": false,
        "description": "IANA timezone name. When set, `pubDate` and `fetched_at` in the response are converted to this zone and `pubDateTZ` carries the zone name. Default `UTC`.",
        "schema": { "type": "string" },
        "examples": { "default": { "value": "Asia/Kolkata" } }
      },
      "Image": {
        "name": "image",
        "in": "query",
        "required": false,
        "description": "`1` → only articles that have an image; `0` → strip `image_url` from response. Default `1`.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "Video": {
        "name": "video",
        "in": "query",
        "required": false,
        "description": "`1` → only articles that have a video; `0` → strip `video_url` from response.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "FullContent": {
        "name": "full_content",
        "in": "query",
        "required": false,
        "description": "`1` → require articles to have full extracted content; `0` → strip `content`. Requires the `is_full_content_user` plan flag.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "RemoveDuplicate": {
        "name": "removeduplicate",
        "in": "query",
        "required": false,
        "description": "`1` → exclude articles marked as duplicates (`is_duplicate=true`). Only applies to data from 2024-07-24 onward.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "Sentiment": {
        "name": "sentiment",
        "in": "query",
        "required": false,
        "description": "Filter on dominant sentiment label. Requires the `is_ml_access` plan flag ∈ {1, 2}. Only applies to data from 2024-01-12 onward.",
        "schema": {
          "type": "string",
          "enum": ["positive", "neutral", "negative"]
        }
      },
      "SentimentScore": {
        "name": "sentiment_score",
        "in": "query",
        "required": false,
        "description": "Minimum confidence for the chosen `sentiment` (1–100, two-decimal precision). Requires `sentiment` to also be set.",
        "schema": { "type": "number", "minimum": 1, "maximum": 100 }
      },
      "Tag": {
        "name": "tag",
        "in": "query",
        "required": false,
        "description": "Comma-separated ML-derived topic tags (e.g. `politics,technology`). Requires `is_ml_access` ∈ {1, 2}. Only applies to data from 2024-01-12 onward. Tag vocabulary differs for crypto vs general news.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Region": {
        "name": "region",
        "in": "query",
        "required": false,
        "description": "Comma-separated ML-extracted geographic regions. Each value may itself contain `-`-joined sub-regions (`paris-france`). Requires `is_ml_access` = 2. Only applies to data from 2024-01-29 onward.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Organization": {
        "name": "organization",
        "in": "query",
        "required": false,
        "description": "Comma-separated ML-extracted organization names. Requires `is_ml_access` = 2. Only applies to data from 2024-05-24 onward.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Creator": {
        "name": "creator",
        "in": "query",
        "required": false,
        "description": "Comma-separated author/byline names.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Datatype": {
        "name": "datatype",
        "in": "query",
        "required": false,
        "description": "Comma-separated source types (e.g. `news, blog, podcast`). Only applies to data from 2025-11-28 onward.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Coin": {
        "name": "coin",
        "in": "query",
        "required": false,
        "description": "Comma-separated cryptocurrency tickers (e.g. `btc,eth`). Validated against the crypto-keyword vocabulary.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Symbol": {
        "name": "symbol",
        "in": "query",
        "required": false,
        "description": "Comma-separated financial-instrument symbols (e.g. `AAPL,MSFT`). Validated against the configured ticker file.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 5 }
      },
      "Interval": {
        "name": "interval",
        "in": "query",
        "required": false,
        "description": "Bucket size for count endpoints. `all` returns a single total; `hour` and `day` return a time-bucketed histogram.",
        "schema": {
          "type": "string",
          "enum": ["all", "hour", "day"],
          "default": "all"
        }
      },
      "Size": {
        "name": "size",
        "in": "query",
        "required": false,
        "description": "Number of articles per page. 1 to `max_response_limit` (50 paid, 10 free).",
        "schema": { "type": "integer", "minimum": 1, "maximum": 50 }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "required": false,
        "description": "Pagination cursor. For `sort=pubdateasc|pubdatedesc` (default), pass the value of the previous response's `nextPage` (an opaque 19-character timestamp). For `sort=relevancy|source|fetched_at`, pass an integer page number (max `10000 / size`).",
        "schema": { "type": "string" },
        "examples": {
          "cursor":  { "value": "1714502400000000000" },
          "integer": { "value": "2" }
        }
      },
      "Sort": {
        "name": "sort",
        "in": "query",
        "required": false,
        "description": "Result ordering. `pubdatedesc` (default) and `pubdateasc` use cursor pagination; the others use integer pagination with a 10k-result ceiling.",
        "schema": {
          "type": "string",
          "enum": ["pubdatedesc", "pubdateasc", "relevancy", "source", "fetched_at"],
          "default": "pubdatedesc"
        }
      },
      "CountSort": {
        "name": "sort",
        "in": "query",
        "required": false,
        "description": "Result ordering for count endpoints. Only `pubdateasc` and `pubdatedesc` are valid.",
        "schema": {
          "type": "string",
          "enum": ["pubdatedesc", "pubdateasc"],
          "default": "pubdatedesc"
        }
      },
      "Adv": {
        "name": "adv",
        "in": "query",
        "required": false,
        "description": "`1` → response includes the `*_id` fields (`link_hash`, `domain_id`, `country_id`, `category_id`, `language_id`). Default `0`.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "ExcludeField": {
        "name": "excludefield",
        "in": "query",
        "required": false,
        "description": "Comma-separated response field names to omit. `article_id` cannot be excluded. Only fields listed in this endpoint's response model are accepted.",
        "explode": false,
        "style": "form",
        "schema": { "type": "array", "items": { "type": "string" }, "maxItems": 20 },
        "examples": { "default": { "value": "content,keywords" } }
      },
      "Webhook": {
        "name": "webhook",
        "in": "query",
        "required": false,
        "description": "`1` → on any non-200 response, the HTTP status is rewritten to 202 (the JSON body is unchanged). Used by webhook delivery systems that treat 4xx/5xx as terminal.",
        "schema": { "type": "string", "enum": ["0", "1"] }
      },
      "LogAccessKey": {
        "name": "access_key",
        "in": "query",
        "required": true,
        "description": "Server-side admin secret (`LOG_VIEW_SECRET`).",
        "schema": { "type": "string" }
      }
    },

    "headers": {
      "XApiLimitRemaining": {
        "description": "Daily/credit budget remaining after this call (`api_limit + extra_credit − api_limit_count − consumption_rate(endpoint)`). Floored at 0. The lowercase variant `x_api_limit_remaining` is also sent for legacy clients.",
        "schema": { "type": "integer", "minimum": 0 }
      },
      "XRateLimitRemaining": {
        "description": "Sliding 15-minute rate-limit budget remaining for the caller's tier. Mirrored as `x_rate_limit_remaining`.",
        "schema": { "type": "integer", "minimum": 0 }
      }
    },

    "schemas": {
      "ErrorCode": {
        "type": "string",
        "description": "Stable wire-visible error code. Treat as a closed enum for switching on errors.",
        "enum": [
          "Unauthorized",
          "ServerError",
          "TooManyRequests",
          "InvalidRequest",
          "AccessDenied",
          "ServiceUnavailable ",
          "NotFound",
          "UnsupportedFilter",
          "FilterLimitExceed",
          "UnsupportedParameter",
          "TooManyQueryFilter",
          "TooManyFilter",
          "TooManyLogicalOperator",
          "UnsupportedDateFormat",
          "UnsupportedQueryLength",
          "UnsupportedQInTitleLength",
          "UnsupportedQInMetaLength",
          "RateLimitExceeded",
          "ApiLimitExceeded",
          "MalformedUrl",
          "MaxPageLimitExceeded",
          "MalformedQuery"
        ]
      },

      "ErrorDetail": {
        "type": "object",
        "required": ["message", "code"],
        "properties": {
          "message": { "type": "string", "description": "Human-readable error description, translated per `Accept-Language`." },
          "code":    { "$ref": "#/components/schemas/ErrorCode" }
        }
      },

      "InvalidFilterDetail": {
        "type": "object",
        "description": "Returned when a filter value rejects against a vocabulary (countries, domains, coins, tags, …).",
        "required": ["message", "code"],
        "properties": {
          "message":          { "type": "string" },
          "code":             { "$ref": "#/components/schemas/ErrorCode" },
          "invalid_domain":   { "type": "string" },
          "invalid_country":  { "type": "string" },
          "invalid_category": { "type": "string" },
          "invalid_language": { "type": "string" },
          "invalid_coin":     { "type": "string" },
          "invalid_ticker":   { "type": "string" },
          "invalid_tag":      { "type": "string" },
          "invalid_region":   { "type": "string" },
          "invalid_datatype": { "type": "string" },
          "invalid_field":    { "type": "string" },
          "invalid_id":       { "type": "string" },
          "suggestion":       { "type": "array", "items": { "type": "string" } }
        }
      },

      "ErrorEnvelope": {
        "type": "object",
        "description": "Generic error response shape. `results` is EITHER an `ErrorDetail` object, OR an `InvalidFilterDetail` object, OR an array of `InvalidFilterDetail` items (returned by validators that aggregate multiple invalid values).",
        "required": ["status", "results"],
        "properties": {
          "status":  { "type": "string", "const": "error" },
          "results": {
            "oneOf": [
              { "$ref": "#/components/schemas/ErrorDetail" },
              { "$ref": "#/components/schemas/InvalidFilterDetail" },
              { "type": "array", "items": { "$ref": "#/components/schemas/InvalidFilterDetail" } }
            ]
          }
        }
      },

      "SentimentStats": {
        "type": "object",
        "description": "Percentage breakdown of the sentiment model output. The three values sum to exactly 100.00 (server-rounded).",
        "required": ["positive", "neutral", "negative"],
        "properties": {
          "positive": { "type": "number", "minimum": 0, "maximum": 100 },
          "neutral":  { "type": "number", "minimum": 0, "maximum": 100 },
          "negative": { "type": "number", "minimum": 0, "maximum": 100 }
        }
      },

      "Article": {
        "type": "object",
        "description": "A news article from the `latest`, `news`, or `archive` endpoints. Fields with `_id` suffix are only present when `adv=1`.",
        "required": ["article_id"],
        "properties": {
          "article_id":      { "type": "string", "description": "32-character hex identifier." },
          "link_hash":       { "type": ["string", "null"], "description": "Present only when `adv=1`." },
          "link":            { "type": ["string", "null"], "format": "uri" },
          "title":           { "type": ["string", "null"] },
          "description":     { "type": ["string", "null"] },
          "content":         { "type": ["string", "null"], "description": "Returned only when the caller has `is_full_content_user`. Otherwise the field is replaced by a marketing string." },
          "keywords":        { "type": ["array", "null"], "items": { "type": "string" } },
          "creator":         { "type": ["array", "null"], "items": { "type": "string" } },
          "language":        { "type": ["string", "null"] },
          "language_id":     { "type": ["integer", "null"] },
          "country":         { "type": ["array", "null"], "items": { "type": "string" } },
          "country_id":      { "type": ["array", "null"], "items": { "type": "integer" } },
          "category":        { "type": ["array", "null"], "items": { "type": "string" } },
          "category_id":     { "type": ["array", "null"], "items": { "type": "integer" } },
          "datatype":        { "type": ["string", "null"], "examples": ["news", "blog", "podcast"] },
          "pubDate":         { "type": ["string", "null"], "format": "date-time", "description": "Publish timestamp in `YYYY-MM-DD HH:MM:SS`. Converted to the requested `timezone` when set." },
          "pubDateTZ":       { "type": "string", "examples": ["UTC", "Asia/Kolkata"] },
          "fetched_at":      { "type": ["string", "null"], "format": "date-time" },
          "image_url":       { "type": ["string", "null"], "format": "uri" },
          "video_url":       { "type": ["string", "null"], "format": "uri" },
          "source_id":       { "type": ["string", "null"] },
          "domain_id":       { "type": ["integer", "null"] },
          "source_name":     { "type": ["string", "null"] },
          "source_priority": { "type": ["integer", "null"] },
          "source_url":      { "type": ["string", "null"], "format": "uri" },
          "source_icon":     { "type": ["string", "null"], "format": "uri" },
          "sentiment":       { "type": ["string", "null"], "enum": ["positive", "neutral", "negative", null] },
          "sentiment_stats": { "anyOf": [ { "$ref": "#/components/schemas/SentimentStats" }, { "type": "null" }, { "type": "string" } ] },
          "ai_tag":          { "type": ["array", "null"], "items": { "type": "string" } },
          "ai_region":       { "type": ["array", "null"], "items": { "type": "string" } },
          "ai_org":          { "type": ["array", "null"], "items": { "type": "string" } },
          "ai_summary":      { "type": ["string", "null"] },
          "duplicate":       { "type": "boolean", "default": false }
        }
      },

      "CryptoArticle": {
        "type": "object",
        "description": "A crypto article from `/1/crypto`. Same shape as `Article` minus `country*`, `category*`, `ai_region`, `ai_org`, `ai_summary`, `datatype`; plus `coin`.",
        "required": ["article_id"],
        "properties": {
          "article_id":      { "type": "string" },
          "link_hash":       { "type": ["string", "null"] },
          "link":            { "type": ["string", "null"], "format": "uri" },
          "title":           { "type": ["string", "null"] },
          "description":     { "type": ["string", "null"] },
          "content":         { "type": ["string", "null"] },
          "keywords":        { "type": ["array", "null"], "items": { "type": "string" } },
          "creator":         { "type": ["array", "null"], "items": { "type": "string" } },
          "coin":            { "type": ["array", "null"], "items": { "type": "string" } },
          "language":        { "type": ["string", "null"] },
          "language_id":     { "type": ["integer", "null"] },
          "pubDate":         { "type": ["string", "null"], "format": "date-time" },
          "pubDateTZ":       { "type": "string" },
          "fetched_at":      { "type": ["string", "null"], "format": "date-time" },
          "image_url":       { "type": ["string", "null"], "format": "uri" },
          "video_url":       { "type": ["string", "null"], "format": "uri" },
          "source_id":       { "type": ["string", "null"] },
          "domain_id":       { "type": ["integer", "null"] },
          "source_name":     { "type": ["string", "null"] },
          "source_priority": { "type": ["integer", "null"] },
          "source_url":      { "type": ["string", "null"], "format": "uri" },
          "source_icon":     { "type": ["string", "null"], "format": "uri" },
          "sentiment":       { "type": ["string", "null"] },
          "sentiment_stats": { "anyOf": [ { "$ref": "#/components/schemas/SentimentStats" }, { "type": "null" } ] },
          "ai_tag":          { "type": ["array", "null"], "items": { "type": "string" } },
          "duplicate":       { "type": "boolean" }
        }
      },

      "MarketArticle": {
        "type": "object",
        "description": "A financial-market article from `/1/market`. Like `Article` but with a `symbol` array and no `category*`, `ai_region`.",
        "required": ["article_id"],
        "properties": {
          "article_id":      { "type": "string" },
          "link":            { "type": ["string", "null"], "format": "uri" },
          "title":           { "type": ["string", "null"] },
          "description":     { "type": ["string", "null"] },
          "content":         { "type": ["string", "null"] },
          "keywords":        { "type": ["array", "null"], "items": { "type": "string" } },
          "creator":         { "type": ["array", "null"], "items": { "type": "string" } },
          "symbol":          { "type": ["array", "null"], "items": { "type": "string" } },
          "language":        { "type": ["string", "null"] },
          "country":         { "type": ["array", "null"], "items": { "type": "string" } },
          "datatype":        { "type": ["string", "null"] },
          "pubDate":         { "type": ["string", "null"], "format": "date-time" },
          "pubDateTZ":       { "type": "string" },
          "fetched_at":      { "type": ["string", "null"], "format": "date-time" },
          "image_url":       { "type": ["string", "null"], "format": "uri" },
          "video_url":       { "type": ["string", "null"], "format": "uri" },
          "source_id":       { "type": ["string", "null"] },
          "source_name":     { "type": ["string", "null"] },
          "source_url":      { "type": ["string", "null"], "format": "uri" },
          "source_icon":     { "type": ["string", "null"], "format": "uri" },
          "sentiment":       { "type": ["string", "null"] },
          "sentiment_stats": { "anyOf": [ { "$ref": "#/components/schemas/SentimentStats" }, { "type": "null" } ] },
          "ai_tag":          { "type": ["array", "null"], "items": { "type": "string" } },
          "ai_org":          { "type": ["array", "null"], "items": { "type": "string" } },
          "ai_summary":      { "type": ["string", "null"] },
          "duplicate":       { "type": "boolean" }
        }
      },

      "Source": {
        "type": "object",
        "required": ["id"],
        "properties": {
          "id":            { "type": "string", "description": "Canonical source identifier (use as `domain` value)." },
          "name":          { "type": ["string", "null"], "description": "Display name." },
          "url":           { "type": ["string", "null"], "format": "uri" },
          "icon":          { "type": ["string", "null"], "format": "uri" },
          "priority":      { "type": ["integer", "null"] },
          "description":   { "type": ["string", "null"] },
          "category":      { "type": ["array", "null"], "items": { "type": "string" } },
          "language":      { "type": ["array", "null"], "items": { "type": "string" } },
          "country":       { "type": ["array", "null"], "items": { "type": "string" } },
          "total_article": { "type": ["integer", "null"] },
          "last_fetch":    { "type": ["string", "null"], "format": "date-time" }
        }
      },

      "Topic": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id":   { "type": "integer" },
          "name": { "type": "string" }
        }
      },

      "ArticleListEnvelope": {
        "type": "object",
        "required": ["status", "totalResults", "results"],
        "properties": {
          "status":       { "type": "string", "const": "success" },
          "totalResults": { "type": "integer", "minimum": 0, "description": "Total matches across all pages." },
          "results":      { "type": "array", "items": { "$ref": "#/components/schemas/Article" } },
          "nextPage":     { "type": ["string", "null"], "description": "Pass back as `?page=...` to fetch the next page. `null` when no more pages exist." }
        }
      },

      "CryptoArticleListEnvelope": {
        "type": "object",
        "required": ["status", "totalResults", "results"],
        "properties": {
          "status":       { "type": "string", "const": "success" },
          "totalResults": { "type": "integer", "minimum": 0 },
          "results":      { "type": "array", "items": { "$ref": "#/components/schemas/CryptoArticle" } },
          "nextPage":     { "type": ["string", "null"] }
        }
      },

      "MarketArticleListEnvelope": {
        "type": "object",
        "required": ["status", "totalResults", "results"],
        "properties": {
          "status":       { "type": "string", "const": "success" },
          "totalResults": { "type": "integer", "minimum": 0 },
          "results":      { "type": "array", "items": { "$ref": "#/components/schemas/MarketArticle" } },
          "nextPage":     { "type": ["string", "null"] }
        }
      },

      "SourceListEnvelope": {
        "type": "object",
        "required": ["status", "totalResults", "results"],
        "properties": {
          "status":       { "type": "string", "const": "success" },
          "totalResults": { "type": "integer", "minimum": 0 },
          "results":      { "type": "array", "items": { "$ref": "#/components/schemas/Source" } },
          "nextPage":     { "type": ["null"] }
        }
      },

      "CountSummaryEnvelope": {
        "type": "object",
        "description": "Shape returned when `interval=all`.",
        "required": ["status", "results"],
        "properties": {
          "status":  { "type": "string", "const": "success" },
          "results": {
            "type": "object",
            "required": ["count"],
            "properties": { "count": { "type": "integer", "minimum": 0 } }
          }
        }
      },

      "CountBucketEnvelope": {
        "type": "object",
        "description": "Shape returned when `interval=hour|day`.",
        "required": ["status", "results"],
        "properties": {
          "status":  { "type": "string", "const": "success" },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["dateTime", "count"],
              "properties": {
                "dateTime": { "type": "string", "format": "date-time" },
                "count":    { "type": "integer", "minimum": 0 }
              }
            }
          },
          "nextPage": { "type": ["string", "null"] }
        }
      },

      "UserAccessLogEnvelope": {
        "type": "object",
        "required": ["status", "result"],
        "properties": {
          "status": { "type": "string", "const": "success" },
          "result": {
            "type": "object",
            "required": ["apikey", "name", "stats"],
            "properties": {
              "apikey":  { "type": "string" },
              "name":    { "type": "string" },
              "stats":   {
                "type": "array",
                "description": "Last 30 days, newest first.",
                "items": {
                  "type": "object",
                  "required": ["date", "count"],
                  "properties": {
                    "date":  { "type": "string", "format": "date" },
                    "count": { "type": "integer", "minimum": 0 }
                  }
                }
              },
              "details": {
                "type": "array",
                "description": "Tail of the current day's raw log entries.",
                "items": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "dateTime":  { "type": "string", "format": "date-time" },
                      "ipAddress": { "type": ["string", "null"] },
                      "url":       { "type": "string" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },

    "responses": {
      "BadRequest": {
        "description": "Request is malformed at the schema level (missing mandatory filter, archive missing `q`-class filter, etc.).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "archiveFilterMissing": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "At least one filter is must i.e : category, country, domain, domainurl, full_content, id, url, image, language, prioritydomain, q, qinmeta, qintitle, video",
                    "code": "UnsupportedParameter"
                  }
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "API key missing, invalid, restricted by IP/domain, or plan does not include the endpoint.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "missing": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "The API Key is missing. Please sign up to receive your free API key at: https://newsdata.io/register",
                    "code": "Unauthorized"
                  }
                }
              },
              "invalid": {
                "value": {
                  "status": "error",
                  "results": { "message": "The provided API key is not valid.", "code": "Unauthorized" }
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Plan does not entitle the caller to this endpoint (`/archive`, `/market`, `/count`, etc.) or to the requested feature flag (sentiment, region, organization, ai_summary).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "archiveDenied": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "Access Denied! You need to upgrade your plan in order to access the Archive endpoint.",
                    "code": "AccessDenied"
                  }
                }
              }
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "A filter value failed validation, two mutually-exclusive filters were sent, or a filter is used outside its supported date range.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "invalidDomain": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "The domain you provided does not exist in our database.",
                    "code": "UnsupportedFilter",
                    "invalid_domain": "bogus.example"
                  }
                }
              },
              "mutuallyExclusive": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "Only one query filter is allowed at a time. Please select either q, qInTitle, or qInMeta.",
                    "code": "TooManyQueryFilter"
                  }
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Either the per-IP DDoS limit, the per-tier rate limit, or the per-user daily quota was exceeded.",
        "headers": {
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/XRateLimitRemaining" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "ddos": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "You have reached the maximum number of requests allowed within a short period. Please attempt again later.",
                    "code": "TooManyRequests"
                  }
                }
              },
              "rateLimit": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "Rate limit exceeded. Please try your request again later.",
                    "code": "RateLimitExceeded"
                  }
                }
              },
              "creditLimit": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "You exceeded your assigned API credits, please check your plan and billing details.",
                    "code": "ApiLimitExceeded"
                  }
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Path does not exist or referenced resource (e.g. topic id) is unknown.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
          }
        }
      },
      "ServerError": {
        "description": "Internal error. The response body always uses the generic envelope with code `ServerError`.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "default": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "The server connection is unavailable at this time. Please try again later.",
                    "code": "ServerError"
                  }
                }
              }
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Maintenance mode is active for this endpoint OR the in-process admission gate rejected the request under overload. NOTE: the error `code` is the literal `\"ServiceUnavailable \"` with a trailing space (legacy wire contract).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" },
            "examples": {
              "maintenance": {
                "value": {
                  "status": "error",
                  "results": {
                    "message": "The system is under maintenance. Please check back later.",
                    "code": "ServiceUnavailable "
                  }
                }
              }
            }
          }
        }
      }
    },

    "examples": {
      "ArticleListExample": {
        "summary": "Single latest-news result with adv=0",
        "value": {
          "status": "success",
          "totalResults": 12384,
          "results": [
            {
              "article_id": "8c2cf3e6abf1f5b7d3a4e9bb1aa00f01",
              "link": "https://www.bbc.com/news/world-asia-india-12345678",
              "title": "Example headline",
              "description": "Short summary of the article body.",
              "content": null,
              "keywords": ["india", "elections"],
              "creator": ["Reporter Name"],
              "language": "english",
              "country": ["india"],
              "category": ["politics"],
              "datatype": "news",
              "pubDate": "2026-05-12 16:42:00",
              "pubDateTZ": "UTC",
              "fetched_at": "2026-05-12 16:43:15",
              "image_url": "https://ichef.bbci.co.uk/news/.../example.jpg",
              "video_url": null,
              "source_id": "bbc",
              "source_name": "Bbc",
              "source_priority": 4321,
              "source_url": "https://www.bbc.com",
              "source_icon": "https://cdn.newsdata.io/icons/bbc.png",
              "sentiment": "neutral",
              "sentiment_stats": { "positive": 18.40, "neutral": 64.20, "negative": 17.40 },
              "ai_tag": ["politics", "government"],
              "ai_region": ["delhi,india"],
              "ai_org": ["Election Commission"],
              "ai_summary": null,
              "duplicate": false
            }
          ],
          "nextPage": "1715533320000000000"
        }
      },
      "SourceListExample": {
        "summary": "One source result",
        "value": {
          "status": "success",
          "totalResults": 1,
          "results": [
            {
              "id": "bbc",
              "name": "Bbc",
              "url": "https://www.bbc.com",
              "icon": "https://cdn.newsdata.io/icons/bbc.png",
              "priority": 4321,
              "description": "British public-service broadcaster.",
              "category": ["top", "world"],
              "language": ["english"],
              "country": ["united kingdom"],
              "total_article": 1834291,
              "last_fetch": "2026-05-12 16:40:00"
            }
          ],
          "nextPage": null
        }
      },
      "CountSummaryExample": {
        "summary": "interval=all (default)",
        "value": {
          "status": "success",
          "results": { "count": 4231 }
        }
      },
      "CountBucketExample": {
        "summary": "interval=hour",
        "value": {
          "status": "success",
          "results": [
            { "dateTime": "2026-05-12 16:00:00", "count": 142 },
            { "dateTime": "2026-05-12 15:00:00", "count": 187 },
            { "dateTime": "2026-05-12 14:00:00", "count": 165 }
          ],
          "nextPage": "1715518800000000000"
        }
      }
    }
  }
}