---
title: "Bing Search API Alternative: Web Results as JSON"
description: "Replace Microsoft's retired Bing Search API with three search endpoints at 1 credit per call. Parameters compared, real responses, and where results differ."
url: https://perceptlayer.com/blog/bing-search-api-alternative-json
---

[Blog](https://perceptlayer.com/blog) / [API guides](https://perceptlayer.com/blog/category/api-guides)

# Bing Search API Alternative: Web Results as JSON

5 Aug 2026 Perceptlayer 8 min read

Microsoft retired its Bing Search APIs on August 11, 2025, so any code still calling them has to move. One replacement is `GET /v1/bing/search`, which returns Bing web results as JSON for 1 credit per successful call. If you'd rather change engines, `/v1/brave/search` and `/v1/duckduckgo/search` use the same key and the same cost. This guide compares the three by parameters and response shape.

## Key takeaways

-   Microsoft retired the Bing Search APIs on August 11, 2025; `GET /v1/bing/search` takes one required parameter, `query`, and returns web results as JSON.
-   Brave ( `/v1/brave/search`) and DuckDuckGo ( `/v1/duckduckgo/search`) offer the same key and cost (1 credit per call) but different parameters and fields from Bing's, so each needs its own small mapping.
-   Microsoft's `q`, `count`, `offset` and `webPages.value[]` map to `query`, `num`, `page` and `data[]`; the table in the migration section lists each pair.
-   Result sets differ by engine, and so do page-size fields. In the one recorded query below, the Bing and Brave top results did not overlap, which is an example and not a benchmark.
-   Failed calls cost nothing and return `X-Credits-Used: 0`; the free tier is 1,000 credits a month, so 1,000 test queries cost no money.

## Why did the official Bing Search API go away?

Microsoft announced the retirement in May 2025 and set August 11, 2025 as the date existing instances were decommissioned, with the product closed to new sign-ups ( [Microsoft Lifecycle announcement](https://learn.microsoft.com/en-us/lifecycle/announcements/bing-search-api-retirement), 2025). Microsoft pointed customers to Bing grounding in its Azure AI Agents offering instead ( [The Register](https://www.theregister.com/2025/05/15/bing_search_apis_retired/), 2025).

That route fits if your search call already lives inside an Azure agent. It does not help if you want a plain HTTP call that returns a list of links and snippets to your own code, which is what most pipelines using the old API did.

The endpoints below are Perceptlayer's own, not Microsoft's. The path, parameters and response shape are different, so expect to change your client code and not only the URL.

## How do Microsoft's old names map to the new endpoint?

Microsoft's Bing Web Search API v7 used `q`, `count`, `offset`, `safeSearch` and `setLang` as query parameters and returned results under `webPages.value[]` with `name`, `url` and `snippet` ( [Microsoft query parameters](https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/reference/query-parameters) and [response objects](https://learn.microsoft.com/en-us/bing/search-apis/bing-web-search/reference/response-objects), 2025). The table pairs each with its closest counterpart on `GET /v1/bing/search`.

| Microsoft v7 (old) | `/v1/bing/search` (new) | What changes |
| --- | --- | --- |
| `q` | `query` | Renamed. Microsoft allowed the `site:` operator inside `q`; here `site` is its own parameter |
| `count` | `num` | Renamed. Microsoft documented a default of 10 and a maximum of 50; this endpoint's `num` limits aren't documented, so test before relying on them |
| `offset` (zero-based count of results to skip) | `page` (integer) | Skip-count paging becomes page-number paging; recompute your loop |
| `safeSearch`: `Off`, `Moderate`, `Strict` | `safe`: `off`, `moderate`, `strict` | Renamed and lower-cased |
| `setLang`, `mkt` | `hl` | One language parameter; its accepted values aren't documented here |
| `Ocp-Apim-Subscription-Key` header | `X-API-Key` header | Different key, see [authentication](https://docs.perceptlayer.com/authentication) |
| `webPages.value[]` | `data[]` | The list moves to the top level |
| `name` | `title` | Renamed |
| `snippet` | `description` | Renamed |
| `url` | `url` | Same name |
| `displayUrl` | `domain` | Not the same thing: `domain` is the host name |
| `webPages.totalEstimatedMatches` | `results_on_page` | Different meaning: a count for this page, not an estimate for the query |

Microsoft's other answer types (images, news, `relatedSearches` and the rest) don't appear in the recorded Bing response below, which holds only `data`, `results_on_page` and `source`. If your code read those answers, check whether you still need them.

## What does the Bing search endpoint take?

`GET /v1/bing/search` has one required parameter, `query` (up to 500 characters), and six optional ones for paging, site and file-type filtering, safe search, language and raw HTML. A successful call costs 1 credit. The documented parameters are in the table, and the [Bing API page](https://perceptlayer.com/apis/bing) has a playground where you can run it with your key.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `query` | string | yes | Up to 500 characters |
| `page` | integer | no |  |
| `num` | integer | no |  |
| `site` | string | no | Up to 253 characters |
| `filetype` | string | no | Up to 20 characters |
| `safe` | string | no | One of `off`, `moderate`, `strict` |
| `hl` | string | no | Up to 20 characters |
| `include_html` | boolean | no |  |

Only the types and limits above are documented, so this guide doesn't state defaults or page sizes. Check them in the playground before you rely on them.

### Request and response

The request is one GET with your key in the `X-API-Key` header (see [authentication](https://docs.perceptlayer.com/authentication)):

```bash
curl "https://api.perceptlayer.com/v1/bing/search?query=best+web+scraping+api" \
  -H "X-API-Key: $API_KEY"
```

This is an excerpt of the recorded response for that exact request:

```json
{
  "data": [
    {
      "position": 1,
      "title": "Best",
      "description": "Sep 14, 2026 · Надійний інтернет-провайдер Best ⭐️ Головна ⚡️Швидкість від 200 Мбіт/с до 1 Гбіт/с ⭐️ Працює при …",
      "url": "https://best.net.ua/",
      "domain": "best.net.ua"
    },
    …
  ],
  "results_on_page": 10,
  "source": "bing"
}
```

Each result has `position`, `title`, `description`, `url` and `domain`. The list sits under `data`, and `results_on_page` says how many results this page held. The snippet text is `description`, and the link is `url`.

## Which parameters change when you switch engines?

The three endpoints cover the same job but name their inputs differently: Bing uses `query`, Brave uses `query` or `q`, and DuckDuckGo uses `q` only. Paging also differs, with `page` on Bing and Brave and `start` on DuckDuckGo. Here are the documented parameters side by side, all at 1 credit per call.

|  | Bing | Brave | DuckDuckGo |
| --- | --- | --- | --- |
| Path | `/v1/bing/search` | `/v1/brave/search` | `/v1/duckduckgo/search` |
| Required | `query` | `query` | `q` |
| Paging | `page`, `num` | `page` | `start` |
| Language or region | `hl` | `language` | `kl` |
| Safe search | `safe`: `off`, `moderate`, `strict` | not listed | `safe`: boolean |
| Site filter | `site` | not listed | not listed |
| File type | `filetype` | not listed | not listed |
| Date filter | not listed | not listed | `df`: `d`, `w`, `m`, `y` |
| Raw HTML | `include_html` | `include_html` | not listed |
| Credits | 1 | 1 | 1 |

"Not listed" means the parameter isn't in the endpoint's documented list. It doesn't prove the engine can't do it, but you can't pass it through this endpoint. If your old code relied on `site` or `filetype`, Bing is the closest match.

Brave's `language` accepts words ( `english`, `deutsch`, `french` and others), two-letter codes ( `en`, `de`, `fr`) or `all`. The recorded DuckDuckGo request uses `kl=us-en`; `kl` is a string of up to 10 characters.

## How does each response look?

Every engine returns JSON with a ranked list, but the field names and the extras around it differ. Bing returns the leanest body. Brave adds related searches and related questions. DuckDuckGo wraps everything in `success`, `data` and `meta`, and adds pagination and a `knowledge_graph` block.

|  | Bing | Brave | DuckDuckGo |
| --- | --- | --- | --- |
| List | `data[]` | `organic_results[]` | `data.organic_results[]` |
| URL field | `url` | `link` | `link` |
| Text field | `description` | `snippet` | `snippet` |
| Extra per-result fields | `domain` | `redirect_link`, `displayed_link`, `source` | `display_url`, `favicon` |
| Beyond the list | `results_on_page` | `related_searches`, `related_questions`, `results_count` | `pagination`, `knowledge_graph`, `meta` |

Here is an excerpt of the recorded Brave response for the same query, `best web scraping api`:

```json
{
  "search_information": {
    "query_displayed": "best web scraping api"
  },
  "organic_results": [
    {
      "position": 1,
      "title": "…The Best Web Scraping API",
      …
    }
  ],
  "related_questions": [
    {
      "question": "What is the best web scraping API in 2026?",
      …
    }
  ],
  "results_count": 20,
  "source": "brave"
}
```

And the recorded DuckDuckGo response for `q=laravel+taylor+otwell&kl=us-en&start=0`, trimmed to the pagination block:

```json
{
  "success": true,
  "data": {
    "search_parameters": {
      "q": "laravel taylor otwell",
      "kl": "us-en",
      "safe": true,
      "start": 0
    },
    …
    "pagination": {
      "current": 1,
      "has_more": true,
      "next_start": 10,
      "next": 2
    }
  },
  "meta": {
    "duration_ms": 2169,
    "endpoint_family": "web_search"
  }
}
```

DuckDuckGo gives you `has_more` and `next_start`, so a page loop can follow the response and doesn't need a guessed offset. One detail to handle: the recorded snippet contains the HTML entity `&amp;`, so decode snippets before you show them or feed them to a model.

## How much should you expect results to change?

Expect the result sets to change in both shape and ranking, and measure it on your own queries. The shape differs by the tables above: Bing's recorded body reports `results_on_page: 10`, Brave's reports `results_count: 20`, and DuckDuckGo reports paging through `pagination`. Rankings differ too, but this guide has one recorded query per engine, which isn't enough to say how much.

Three habits keep the migration honest:

1.  Replay a sample of your real production queries against the old results you saved, and compare the top URLs, not only the count.
2.  Keep `position` in your stored rows so you can compare rankings later.
3.  Use the page-size fields in each body and don't hard-code a page size.

Here is the one example we have. For `best web scraping api`, the recorded Bing top two were an internet provider's homepage and a dictionary entry for "best", while the recorded Brave top two were pages about scraping APIs. That is one short, ambiguous query on one day, not a benchmark. For ambiguous queries, add words that pin the topic, or use `site` on the Bing endpoint to restrict results to a domain. Data is returned as found at the time of the call, so rankings can move between runs.

## How do you call all three from one script?

One small normaliser lets the rest of your code ignore which engine answered. This Python script sends the same query to each endpoint, maps each body to `position`, `title`, `url` and `snippet`, and prints the credits the call used from the `X-Credits-Used` header.

```python
import html
import os

import requests

BASE = "https://api.perceptlayer.com"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}

def bing(q):
    r = requests.get(f"{BASE}/v1/bing/search", params={"query": q}, headers=HEADERS, timeout=30)
    r.raise_for_status()
    rows = [(x["position"], x["title"], x["url"], x["description"]) for x in r.json()["data"]]
    return r, rows

def brave(q):
    r = requests.get(f"{BASE}/v1/brave/search", params={"query": q}, headers=HEADERS, timeout=30)
    r.raise_for_status()
    rows = [(x["position"], x["title"], x["link"], x["snippet"]) for x in r.json()["organic_results"]]
    return r, rows

def duckduckgo(q):
    r = requests.get(f"{BASE}/v1/duckduckgo/search", params={"q": q}, headers=HEADERS, timeout=30)
    r.raise_for_status()
    rows = [
        (x["position"], x["title"], x["link"], html.unescape(x["snippet"]))
        for x in r.json()["data"]["organic_results"]
    ]
    return r, rows

for name, fn in [("bing", bing), ("brave", brave), ("duckduckgo", duckduckgo)]:
    resp, rows = fn("best web scraping api")
    print(name, "credits used:", resp.headers.get("X-Credits-Used"))
    for position, title, url, snippet in rows[:3]:
        print(" ", position, title, url)
```

The script handles errors only with `raise_for_status()`. For retries, rate-limit waits and the error codes, see [errors](https://docs.perceptlayer.com/errors) and the related guide on [handling web data API errors in Python](https://perceptlayer.com/blog/handle-web-data-api-errors-python). To fetch more than one page, [paginate safely](https://perceptlayer.com/blog/paginate-web-data-api-python).

## What does a search migration cost in credits?

Each successful call costs 1 credit on all three endpoints, and each page you request is its own call. A job that sends 5,000 queries and reads 3 pages of each makes 15,000 calls, which is 15,000 credits. Failed calls cost nothing and return `X-Credits-Used: 0`, so retries on errors don't add to the bill.

| Workload | Calls | Credits |
| --- | --- | --- |
| Trial: 1,000 queries, 1 page each | 1,000 | 1,000 (the monthly free tier) |
| 20,000 queries, 1 page each | 20,000 | 20,000 |
| 5,000 queries, 3 pages each | 15,000 | 15,000 |

Credits come in prepaid packs. The Starter pack is 10,000 credits, so 20,000 credits needs the Build pack (50,000) or two Starter packs. Purchased credits last 12 months, with no subscription. The [pricing page](https://perceptlayer.com/pricing) has what credits cost; this guide doesn't quote prices because they change.

If you compare against another provider's rate card, count calls the same way: one call per page, not one per query. The [credits docs](https://docs.perceptlayer.com/credits) explain how `X-Credits-Used` and `X-Credits-Remaining` report each call, and [rate limits](https://docs.perceptlayer.com/rate-limits) cover requests per second if you run the migration as a batch.

## How do you start?

Sign up, send the `curl` call from the Bing section with your key, and diff its top URLs against a saved result from your old integration. Free accounts get 1,000 credits a month after email verification, with no credit card, so a first comparison across all three engines costs nothing. [Get a free API key](https://perceptlayer.com/signup), then run the same query on the [Bing](https://perceptlayer.com/apis/bing/search), [Brave](https://perceptlayer.com/apis/brave/search) and [DuckDuckGo](https://perceptlayer.com/apis/duckduckgo/search) search endpoints in the playground.

If you're feeding results to an agent, the same endpoints are available as tools through the MCP server; see [AI agents (MCP)](https://docs.perceptlayer.com/ai-agents). For Google results instead, read the [Google Search API guide](https://perceptlayer.com/blog/google-search-api-json). The [search engines hub](https://perceptlayer.com/verticals/search) lists every search source in the catalogue.

## APIs in this post

-   [Bing **1 credit per call** · 1 endpoint](https://perceptlayer.com/apis/bing)
-   [Brave Search **1 credit per call** · 1 endpoint](https://perceptlayer.com/apis/brave)
-   [DuckDuckGo **1 credit per call** · 1 endpoint](https://perceptlayer.com/apis/duckduckgo)

## Frequently asked questions

### Is the Bing Search API still available?

No. Microsoft retired the Bing Search APIs on August 11, 2025, and existing instances were decommissioned ( [Microsoft Lifecycle announcement](https://learn.microsoft.com/en-us/lifecycle/announcements/bing-search-api-retirement), 2025). Microsoft suggested Bing grounding in Azure AI Agents as its replacement. `GET /v1/bing/search` is a separate endpoint with its own parameters and JSON shape.

### What does one search call cost?

One credit for each successful call on the Bing, Brave and DuckDuckGo search endpoints. Failed calls cost nothing and return `X-Credits-Used: 0`. Every account gets 1,000 free credits a month after verifying an email address, so a first comparison costs nothing. See [pricing](https://perceptlayer.com/pricing) for what credits cost.

### Which endpoint should I use if I need site or file-type filters?

Use the Bing endpoint. It documents `site` (up to 253 characters) and `filetype` (up to 20 characters), and the Brave and DuckDuckGo endpoints list neither.

### Will I get the same results as the old Bing API?

Not guaranteed. Data is returned as found at the time of the call, and result sets can differ by engine. In the one recorded query, Bing and Brave returned different top results, which is an example and not a benchmark. Replay a sample of your real queries and compare URLs before you cut over.

### How do I get more than one page of results?

Bing and Brave take `page`; DuckDuckGo takes `start`, and its response includes `pagination.has_more` and `next_start` so you can follow it. Each page is a separate call at 1 credit. Stop the loop when `has_more` is false or when your credit budget for the job is spent.

search api bing brave duckduckgo serp json

## More from the blog

1.  [API guides](https://perceptlayer.com/blog/category/api-guides) 20 Aug 2026 Perceptlayer 6 min read
    
    ## [DuckDuckGo Search API: Web Results as JSON](https://perceptlayer.com/blog/duckduckgo-search-api-json)
    
    DuckDuckGo's Instant Answer API returns topic summaries, not ranked links. GET /v1/duckduckgo/search returns organic results and paging at 1 credit per query.
    
2.  [API guides](https://perceptlayer.com/blog/category/api-guides) 10 Aug 2026 Perceptlayer 7 min read
    
    ## [Google Search API: Get Search Results as JSON](https://perceptlayer.com/blog/google-search-api-json)
    
    Call GET /v1/google-search/search for organic results as JSON at 1 credit a call. Parameters, a real recorded response and the cost in credits.
    
3.  [API guides](https://perceptlayer.com/blog/category/api-guides) 17 Aug 2026 Perceptlayer 7 min read
    
    ## [Brave Search API: Official Plans or One Multi-Source Key](https://perceptlayer.com/blog/brave-search-api-results-json)
    
    Brave's official Search API has freshness, country and Goggles filters. When it fits, and when one key for Brave, Google and Bing at 1 credit a query wins.
