{"record":{"id":"c6f2548872dfcec5","repo":"decolua/9router","slug":"searchapi-requires-an-api-key","errorCode":null,"errorMessage":"SearchAPI requires an API key","messagePattern":"SearchAPI requires an API key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/search/callers.js","lineNumber":269,"sourceCode":"    if (params.timeRange === \"month\") from.setUTCMonth(from.getUTCMonth() - 1);\n    if (params.timeRange === \"year\") from.setUTCFullYear(from.getUTCFullYear() - 1);\n    body.fromDate = from.toISOString().slice(0, 10);\n    body.toDate = toDate;\n  }\n\n  return {\n    url: resolveBaseUrl(config, params),\n    init: {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${apiKey}` },\n      body: JSON.stringify(body),\n    },\n  };\n}\n\nfunction buildSearchApiRequest(config, params) {\n  const apiKey = params.token;\n  if (!apiKey) throw new Error(\"SearchAPI requires an API key\");\n\n  const qp = new URLSearchParams({\n    engine: params.searchType === \"news\" ? \"google_news\" : \"google\",\n    q: params.query,\n    api_key: apiKey,\n  });\n  if (params.country) qp.set(\"gl\", params.country.toLowerCase());\n  if (params.language) qp.set(\"hl\", params.language);\n\n  const page = toPageNumber(params.offset, params.maxResults);\n  if (page) qp.set(\"page\", String(page));\n\n  return {\n    url: `${resolveBaseUrl(config, params)}?${qp}`,\n    init: {\n      method: \"GET\",\n      headers: { Accept: \"application/json\" },\n    },","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/search/callers.js#L251-L287","documentation":"buildSearchApiRequest builds the request for the SearchAPI (searchapi.io) provider. SearchAPI authenticates via an `api_key` query parameter, so without params.token the request cannot succeed; the builder throws immediately with this message when the token is falsy.","triggerScenarios":"Invoking the SearchAPI search route with params.token undefined/null/empty — no API key saved for the searchapi provider, or a caller constructing params manually without `token`.","commonSituations":"Enabled the SearchAPI provider without pasting a key from searchapi.io; key stored under a different provider entry; dashboard account deleted or rotated leaving an empty value; automation scripts passing partial params.","solutions":["Sign up at searchapi.io, copy the API key, and save it for the SearchAPI provider in the dashboard","Confirm params.token is a non-empty string at the call site","Check the credential is stored under the searchapi provider id, not another provider","If key rotation emptied the field, re-enter the key"],"exampleFix":"// before\nawait search({ provider: 'searchapi', query: 'openai news', searchType: 'news' });\n// after\nawait search({ provider: 'searchapi', query: 'openai news', searchType: 'news', token: process.env.SEARCHAPI_KEY });","handlingStrategy":"validation","validationCode":"if (!params?.token || typeof params.token !== 'string' || !params.token.trim()) {\n  throw new Error('SearchAPI search requires a non-empty token');\n}","typeGuard":"function hasSearchApiToken(params) {\n  return typeof params?.token === 'string' && params.token.trim().length > 0;\n}","tryCatchPattern":"try {\n  const res = await search({ provider: 'searchapi', query, token });\n} catch (err) {\n  if (err.message === 'SearchAPI requires an API key') {\n    // surface a config error to the user; do not retry\n  } else throw err;\n}","preventionTips":["Save the searchapi.io key during provider onboarding","Add a preflight check that every enabled search provider has a token","Avoid building params objects by hand; use a factory that pulls credentials from config"],"tags":["api-key","search","searchapi","configuration"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}