getreadystack's picture
Upload index.html with huggingface_hub
eb24031 verified
Raw History Blame Contribute Delete
43.4 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Bing Search API retired — check your code for free</title>
<meta name="description" content="This free tool flags Google Custom Search and retired Bing API calls and shows the Brave rewrite for each line.">
<style>
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
</style>
<script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/brave-search-api-migration-check"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static!important;justify-content:flex-end;margin:0 0 10px}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style>
</head>
<body>
<div class="wrap">
<h1>Bing Search API retired — check your code for free</h1>
<p class="lede">Brave Search API migration lint: flags Google Custom Search JSON API calls (existing customers must move by 2027-01-01) and retired Bing v7 calls, with the Brave rewrite per line. Runs entirely in your browser — nothing is uploaded.</p>
<textarea id="src" spellcheck="false">// search.js — web lookup for the support-bot RAG agent
const GOOGLE_KEY = process.env.GOOGLE_KEY;
const ENGINE = process.env.CSE_ID;
async function webSearch(query, page = 0) {
const params = new URLSearchParams({
key: GOOGLE_KEY,
cx: ENGINE,
q: query,
num: '10',
start: String(page * 10 + 1),
dateRestrict: 'm6',
siteSearch: 'docs.example.com',
});
const res = await fetch(`https://www.googleapis.com/customsearch/v1?${params}`);
const data = await res.json();
return (data.items || []).map((it) =&gt; ({ title: it.title, url: it.link, text: it.snippet }));
}
module.exports = { webSearch };</textarea>
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
<div id="out" aria-live="polite"></div>
<div id="paid"><strong>Free here:</strong> the file you paste, every one of the 19 rules, every Brave rewrite. <strong>Full version:</strong> workspace-wide scan plus an exportable migration report for the whole repo, to hand to a team or attach to the PR — <a href="https://getreadystack.com/api/buy/cl/polar_cl_BRotSWt4oCajWR07G0RStXi8mYXz3Hcf3rUDN1WGb96" data-polar-checkout data-polar-checkout-theme="light">Get the full version — $29</a> ($29 once · one licence key per person or team seat). US software developers' median pay is $64.44 an hour (BLS).</div>
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
</div>
<script>var BSM_RULES = [
{
"id": "GCS_ENDPOINT",
"sev": "error",
"scope": "any",
"re": "googleapis\\.com/customsearch/v1",
"flags": "i",
"cutoff": "2027-01-01",
"msg": "Google Custom Search JSON API endpoint. Google: closed to new customers; existing customers have until 2027-01-01 to transition.",
"msg_after": "Google Custom Search JSON API endpoint. Google's transition date 2027-01-01 has passed; this call is on borrowed time.",
"fix": "GET https://api.search.brave.com/res/v1/web/search?q=… with header X-Subscription-Token"
},
{
"id": "GCS_CLIENT",
"sev": "error",
"scope": "any",
"re": "build\\(\\s*['\"]customsearch['\"]|google\\.customsearch\\s*\\(|\\.cse\\(\\)\\.list\\(",
"flags": "",
"msg": "Google API client call to the Custom Search JSON API (same 2027-01-01 transition date as the raw endpoint).",
"fix": "Replace the client with a plain HTTPS GET to /res/v1/web/search; Brave has no official Google-style discovery client"
},
{
"id": "BING_V7",
"sev": "error",
"scope": "any",
"re": "api\\.bing\\.microsoft\\.com/v7\\.0|api\\.cognitive\\.microsoft\\.com/bing",
"flags": "i",
"msg": "Bing Search API v7 endpoint. Microsoft retired the Bing Search APIs on 2025-08-11.",
"fix": "https://api.search.brave.com/res/v1/web/search (images: /res/v1/images/search, news: /res/v1/news/search)"
},
{
"id": "BING_KEY_HEADER",
"sev": "error",
"scope": "any",
"re": "Ocp-Apim-Subscription-Key",
"flags": "i",
"msg": "Azure/Bing key header. Brave does not read this header, so the request goes out unauthenticated.",
"fix": "Send the key as header X-Subscription-Token: \u003ckey\u003e"
},
{
"id": "KEY_IN_QUERY",
"sev": "error",
"scope": "search",
"re": "[?\u0026]key=|(^|[\\s{,])['\"]?key['\"]?\\s*:",
"flags": "",
"msg": "API key sent as the ?key= query parameter (Google style). Brave reads the key only from a header.",
"fix": "Remove key from the query; add header X-Subscription-Token"
},
{
"id": "CX_ENGINE_ID",
"sev": "warn",
"scope": "search",
"re": "[?\u0026]cx=|(^|[\\s{,])['\"]?cx['\"]?\\s*[:=]",
"flags": "",
"msg": "cx (Programmable Search Engine ID). Brave has no engine ID; the site list inside that engine is lost silently.",
"fix": "Put the engine's sites into q as site:example.com operators, or use a Brave Goggle"
},
{
"id": "START_INDEX",
"sev": "error",
"scope": "search",
"re": "[?\u0026]start=|(^|[\\s{,])['\"]?start['\"]?\\s*:",
"flags": "",
"msg": "start = Google's 1-based result index (1, 11, 21 …). Brave offset is a 0-based PAGE index, max 9 — passing start through breaks page 2 onwards.",
"fix": "offset = Math.floor((start - 1) / count) (start 11, count 10 → offset 1)"
},
{
"id": "OFFSET_OVER_9",
"sev": "error",
"scope": "search",
"re": "(^|[\\s{,?\u0026])['\"]?offset['\"]?\\s*[:=]\\s*['\"]?(\\d+)",
"flags": "",
"num_group": 2,
"max": 9,
"msg": "offset above 9. Brave's offset is a page index with a maximum of 9.",
"fix": "Use a page index 0–9 (offset × count = results skipped)"
},
{
"id": "COUNT_OVER_20",
"sev": "warn",
"scope": "search",
"re": "(^|[\\s{,?\u0026])['\"]?(count|num)['\"]?\\s*[:=]\\s*['\"]?(\\d+)",
"flags": "",
"num_group": 3,
"max": 20,
"msg": "Page size above 20. Bing allowed count up to 50; Brave web search caps count at 20.",
"fix": "count: 20 and page with offset"
},
{
"id": "DATE_RESTRICT",
"sev": "warn",
"scope": "search",
"re": "dateRestrict",
"flags": "",
"msg": "dateRestrict (d/w/m/y + N) has no Brave twin with the same name.",
"fix": "freshness: pd | pw | pm | py, or a range YYYY-MM-DDtoYYYY-MM-DD for m6 / d3 etc."
},
{
"id": "BING_FRESHNESS",
"sev": "warn",
"scope": "search",
"re": "freshness['\"]?\\s*[:=]\\s*['\"](Day|Week|Month)['\"]",
"flags": "",
"msg": "Bing freshness value (Day/Week/Month). Brave uses short codes.",
"fix": "Day → pd · Week → pw · Month → pm"
},
{
"id": "SITE_SEARCH",
"sev": "warn",
"scope": "search",
"re": "siteSearch",
"flags": "",
"msg": "siteSearch parameter. Brave has no siteSearch parameter.",
"fix": "Append site:example.com to q (siteSearchFilter=e → -site:example.com)"
},
{
"id": "SAFE_PARAM",
"sev": "warn",
"scope": "search",
"re": "(^|[\\s{,?\u0026])['\"]?safe['\"]?\\s*[:=]|safeSearch['\"]?\\s*[:=]",
"flags": "",
"msg": "Google safe= / Bing safeSearch= parameter. Brave's parameter is lowercase safesearch with its own values.",
"fix": "safesearch: off | moderate | strict (Google active → strict)"
},
{
"id": "LANG_REGION",
"sev": "warn",
"scope": "search",
"re": "(^|[\\s{,?\u0026])['\"]?(lr|gl|cr|hl|mkt|setLang)['\"]?\\s*[:=]",
"flags": "",
"msg": "Google lr/gl/cr/hl or Bing mkt/setLang. Brave splits region and language into two parameters.",
"fix": "country: 'DE' (2 letters) + search_lang: 'de' (ISO 639-1)"
},
{
"id": "IMAGE_SEARCH",
"sev": "warn",
"scope": "search",
"re": "searchType['\"]?\\s*[:=]\\s*['\"]image",
"flags": "",
"msg": "searchType=image on the web endpoint. Brave serves images from a separate endpoint.",
"fix": "GET https://api.search.brave.com/res/v1/images/search"
},
{
"id": "RESP_ITEMS",
"sev": "error",
"scope": "search",
"re": "\\.items\\b(?!\\s*\\()|\\[['\"]items['\"]\\]",
"flags": "",
"msg": "Reads Google's items[] array. A Brave response has no items key — this returns undefined/KeyError, not an exception at the call.",
"fix": "data.web?.results ?? []"
},
{
"id": "RESP_LINK_SNIPPET",
"sev": "error",
"scope": "search",
"re": "\\.(link|snippet|htmlSnippet|displayLink)\\b|\\[['\"](link|snippet|htmlSnippet|displayLink)['\"]\\]",
"flags": "",
"msg": "Google result fields link/snippet. Brave results carry url and description.",
"fix": "link → url · snippet → description · displayLink → meta_url.hostname"
},
{
"id": "BING_WEBPAGES",
"sev": "error",
"scope": "search",
"re": "webPages",
"flags": "",
"msg": "Bing response path webPages.value[]. Brave uses web.results[] (name → title, snippet → description).",
"fix": "data.web.results"
},
{
"id": "TOTAL_RESULTS",
"sev": "warn",
"scope": "search",
"re": "totalResults|searchInformation|totalEstimatedMatches",
"flags": "",
"msg": "Total-hit count from Google/Bing. Brave returns no total count.",
"fix": "Stop paging on data.query.more_results_available === false"
}
];</script>
<script>// Brave Search API migration check — one brain for the VS Code extension and the free web page.
(function () {
const RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.BSM_RULES;
// Parameter and response rules only run in files that talk to a search API.
const SEARCH_FILE = /customsearch|googleapis\.com|bing\.microsoft\.com|cognitive\.microsoft\.com\/bing|api\.search\.brave\.com/i;
function check(text, opts) {
opts = opts || {};
const today = String(opts.today || new Date().toISOString()).slice(0, 10);
const isSearch = SEARCH_FILE.test(text);
const lines = String(text).split(/\r?\n/);
const findings = [];
lines.forEach((raw, i) => {
const line = raw.replace(/^\s*(\/\/|#).*$/, '');
if (!line.trim()) return;
for (const r of RULES) {
if (r.scope === 'search' && !isSearch) continue;
const m = new RegExp(r.re, r.flags || '').exec(line);
if (!m) continue;
if (r.max != null && !(Number(m[r.num_group]) > r.max)) continue;
const past = r.cutoff && !isNaN(Date.parse(today)) && today >= r.cutoff;
findings.push({ check: r.id, sev: r.sev, msg: (past && r.msg_after ? r.msg_after : r.msg) + ' → Brave: ' + r.fix,
fix: r.fix, line: i + 1, text: raw.trim() });
}
});
return { findings };
}
const api = { engine: { check }, RULES, RULE_COUNT: RULES.length };
if (typeof module !== 'undefined') module.exports = api;
if (typeof window !== 'undefined') window.BSMENGINE = api;
})();
</script>
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
'use strict';
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }
function summaryLine(res) {
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
}
function toText(res, label) {
const f = (res && res.findings) || [];
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
return lines.join('\n');
}
function toHtml(res) {
const f = (res && res.findings) || [];
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
var BSMREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
if (typeof module !== 'undefined' && module.exports) module.exports = BSMREPORT;
else window.BSMREPORT = BSMREPORT;
</script>
<script>
(function(){
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
today.value=new Date().toISOString().slice(0,10);
function run(){ try { var res=window.BSMENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.BSMREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
go.onclick=run; if (src.value.trim()) run();
})();
</script>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.brave-search-api-migration-check" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'brave-search-api-migration-check',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/brave-search-api-migration-check" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'brave-search-api-migration-check',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.brave-search-api-migration-check</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Brave Search API Migration Check — Google Custom Search &amp; Bing every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"brave-search-api-migration-check",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">Workspace-wide scan plus an exportable migration report for the whole repo, to hand to a team or attach to the PR.</p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><p class="yj-cta-anchor" style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">US software developers' median pay is $64.44 an hour (BLS Occupational Outlook Handbook); a hand trace of every search call, parameter and response field is hours of that.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_BRotSWt4oCajWR07G0RStXi8mYXz3Hcf3rUDN1WGb96" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-29">Updated 2026-09-29</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-29","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku392657_result_card.jpg" alt="Brave Search API Migration Check — Google Custom Search &amp; Bing" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>Migrate from Google Custom Search JSON API to Brave Search API: the eight lines that break</p>
<p>Eight Brave Search API fixes: that is what one 20-line Google Custom Search helper, the kind a developer wires into a RAG agent or a site-search box, needed before it could run on Brave. Five were errors that return wrong or empty results, three were warnings that silently drop a filter.</p>
<p>Why now. Google&#x27;s Custom Search JSON API overview says it plainly: &quot;The Custom Search JSON API is closed to new customers. Existing Custom Search JSON API customers have until January 1, 2027 to transition to an alternative solution.&quot; Microsoft already retired the Bing Search APIs on 2025-08-11, so the usual fallback is gone too.</p>
<p>What a URL swap misses. Changing https://www.googleapis.com/customsearch/v1 to https://api.search.brave.com/res/v1/web/search looks like the whole job. It is one line of eight. On the sample file search.js the check reported:</p>
<ul><li>key: GOOGLE_KEY in the query string. Brave reads the key only from the X-Subscription-Token header.</li><li>cx: ENGINE. Brave has no Programmable Search Engine ID; the sites inside that engine move into q as site: operators or into a Goggle.</li><li>start: String(page * 10 + 1). Google&#x27;s start is a 1-based result index. Brave&#x27;s offset is a 0-based page index with a maximum of 9. The fix is offset = Math.floor((start - 1) / count), which for this file is simply offset: String(page).</li><li>dateRestrict: &#x27;m6&#x27;. Brave&#x27;s freshness takes pd, pw, pm, py or a range like 2026-03-29to2026-09-29.</li><li>siteSearch: &#x27;docs.example.com&#x27;. No such Brave parameter; it becomes site:docs.example.com in q.</li><li>The endpoint itself.</li><li>data.items. A Brave response has no items key; results live in data.web.results.</li><li>it.link and it.snippet. Brave results carry url and description.</li></ul>
<p>None of these throw at the call. The request succeeds, the agent gets an empty list or page 2 fails, and the bug shows up in answers rather than in logs.</p>
<p>The page-2 trap. This is the one to check first. A find-and-replace that keeps Google&#x27;s paging turns start=11 into offset=11. Brave&#x27;s reference caps offset at 9, because it counts pages, not results. A half-migrated file with offset &#x27;11&#x27; produces exactly one finding in the check, and it is the one that breaks your second page.</p>
<p>Bing code has its own list. An 8-line Python newsletter bot on Bing v7 gave 6 findings: the retired endpoint, the Ocp-Apim-Subscription-Key header, count: 50 (Brave caps count at 20), mkt: en-GB (split into country and search_lang), freshness: Week (pw), and data[&quot;webPages&quot;][&quot;value&quot;] (web.results).</p>
<p>How the check works. It is a VS Code extension and a free web page that share one engine with 19 rules. Endpoint and header rules run on every JavaScript, TypeScript or Python file; parameter and response rules only run in files that reference a search endpoint, so a start: in your router config stays quiet. Each finding prints the Brave line to write. The migrated search.js gives 0 findings.</p>
<p>Doing it by hand. The US median pay for software developers is $64.44 an hour (BLS Occupational Outlook Handbook). The work is reading every search helper, mapping each parameter and fixing response parsing, file by file.</p>
<p>Free and full. The free extension and the web page check the open file completely, with every rule and every rewrite. The full version adds a workspace-wide scan and one exportable migration report for the whole repository, for a team or a pull request. Paste your own search helper into the web page first and read the answer before you decide anything.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku392657_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku392657_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Brave Search API Migration Check do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads JavaScript, TypeScript and Python files and flags every line that still uses the Google Custom Search JSON API or the retired Bing Search API v7: endpoints, key headers, cx, start, dateRestrict, siteSearch, safe, lr/gl/mkt, items, link and snippet. Each finding prints the Brave Search API replacement, such as offset = floor((start-1)/count) or web.results instead of items. It runs 19 rules.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is the Brave Search API migration check for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Developers whose app, RAG agent, chatbot or site search calls the Google Custom Search JSON API, which Google closed to new customers and gives existing customers until January 1, 2027 to leave, and teams still carrying Bing Search API v7 code after Microsoft retired it on 2025-08-11. It suits anyone moving that code to the Brave Search API.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just swap the endpoint URL or ask a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Swapping the URL leaves Google&#x27;s parameter meanings in place. Google&#x27;s start is a 1-based result index; Brave&#x27;s offset is a 0-based page index with a maximum of 9, so start=11 passed through breaks page 2. The cx engine ID, dateRestrict, siteSearch and the items/link/snippet response fields also have no same-name Brave twin, and nothing fails at the call itself.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what does the full version add?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free: the open file is checked completely, every line against all 19 rules, each with its Brave rewrite, with no limit on use and no key. The full version, one payment for one licence key, adds a workspace-wide scan and an exportable migration report covering the whole repository, ready to hand to a team or attach to the pull request.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does doing this migration audit by hand cost?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The US median pay for software developers is $64.44 an hour according to the BLS Occupational Outlook Handbook. A manual audit means reading each search helper, mapping every Google or Bing parameter to its Brave equivalent and fixing the response parsing. On the bundled 20-line sample file the check lists 8 findings: 5 errors and 3 warnings.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Brave Search API Migration Check do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads JavaScript, TypeScript and Python files and flags every line that still uses the Google Custom Search JSON API or the retired Bing Search API v7: endpoints, key headers, cx, start, dateRestrict, siteSearch, safe, lr/gl/mkt, items, link and snippet. Each finding prints the Brave Search API replacement, such as offset = floor((start-1)/count) or web.results instead of items. It runs 19 rules."}}, {"@type": "Question", "name": "Who is the Brave Search API migration check for?", "acceptedAnswer": {"@type": "Answer", "text": "Developers whose app, RAG agent, chatbot or site search calls the Google Custom Search JSON API, which Google closed to new customers and gives existing customers until January 1, 2027 to leave, and teams still carrying Bing Search API v7 code after Microsoft retired it on 2025-08-11. It suits anyone moving that code to the Brave Search API."}}, {"@type": "Question", "name": "Why not just swap the endpoint URL or ask a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "Swapping the URL leaves Google's parameter meanings in place. Google's start is a 1-based result index; Brave's offset is a 0-based page index with a maximum of 9, so start=11 passed through breaks page 2. The cx engine ID, dateRestrict, siteSearch and the items/link/snippet response fields also have no same-name Brave twin, and nothing fails at the call itself."}}, {"@type": "Question", "name": "What is free and what does the full version add?", "acceptedAnswer": {"@type": "Answer", "text": "Free: the open file is checked completely, every line against all 19 rules, each with its Brave rewrite, with no limit on use and no key. The full version, one payment for one licence key, adds a workspace-wide scan and an exportable migration report covering the whole repository, ready to hand to a team or attach to the pull request."}}, {"@type": "Question", "name": "What does doing this migration audit by hand cost?", "acceptedAnswer": {"@type": "Answer", "text": "The US median pay for software developers is $64.44 an hour according to the BLS Occupational Outlook Handbook. A manual audit means reading each search helper, mapping every Google or Bing parameter to its Brave equivalent and fixing the response parsing. On the bundled 20-line sample file the check lists 8 findings: 5 errors and 3 warnings."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:392657,slug:"brave-search-api-migration-check",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/robots-txt-ai-crawler-audit"><span class="yj-rel-t">AI Crawler Rules - robots.txt Audit for AI Search</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/play-release-blocker-lint"><span class="yj-rel-t">Google Play Release Blocker Lint - target API 36, Billing 8, 16 KB</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/almalinux-migration-lint"><span class="yj-rel-t">AlmaLinux Migration Lint — CentOS 7/8</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/angular-removed-api-lint"><span class="yj-rel-t">Angular Removed-API Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/bulk-sender-lint"><span class="yj-rel-t">Bulk Sender Lint - the Gmail, Yahoo and Outlook.com rules, checked in your mai</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cms-0057-api-gate"><span class="yj-rel-t">CMS-0057 API Gate</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
</html>