Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Official SDKs for the Microsoft Web IQ API, available in Python and TypeScript. These SDKs give your applications real-time access to web search, videos search, browse, news search, and images search — designed to ground LLM responses with accurate, up-to-date information.
| Python | TypeScript | |
|---|---|---|
| Package | pip install webiq |
npm install @microsoft/webiq |
| Runtime | Python 3.11+ | Node.js 22+ |
| License | MIT | MIT |
Installation
Python
pip install webiq
TypeScript
npm install @microsoft/webiq
Quickstart
Python Guide
import os
from webiq import WebIQClient
from webiq.types import BrowseContentFormat, ContentFormat
with WebIQClient(api_key=os.environ["WEBIQ_API_KEY"]) as client:
# Web search
web = client.web.search(
"Python programming",
max_results=5,
content_format=ContentFormat.passage,
)
for r in web.webResults or []:
print(r.title, r.url)
# Videos search
videos = client.videos.search("machine learning tutorial", max_results=5)
for v in videos.videoResults or []:
print(v.title, v.length)
# Browse a URL
page = client.browse.fetch("https://www.microsoft.com", content_format=BrowseContentFormat.markdown)
print(page.content)
# News search
news = client.news.search("technology", max_results=5)
for item in news.newsResults or []:
print(item.title, "-", item.source)
# Image search
images = client.images.search("golden gate bridge", max_results=5)
for img in images.imageResults or []:
print(img.title, img.url)
TypeScript Guide
import { WebIQClient, BrowseContentFormat, ContentFormat } from '@microsoft/webiq';
async function main() {
const client = new WebIQClient({ apiKey: process.env.WEBIQ_API_KEY });
// Web search
const web = await client.web.search('TypeScript programming', {
maxResults: 5,
contentFormat: ContentFormat.PASSAGE,
});
for (const r of web.webResults ?? []) console.log(r.title, r.url);
// Videos search
const videos = await client.videos.search('machine learning tutorial');
for (const v of videos.videoResults ?? []) console.log(v.title, v.length);
// Browse a URL
const page = await client.browse.fetch('https://www.microsoft.com', {
contentFormat: BrowseContentFormat.MARKDOWN,
});
console.log(page.content);
// News search
const news = await client.news.search('technology', { maxResults: 5 });
for (const item of news.newsResults ?? []) console.log(item.title, '-', item.source);
// Image search
const images = await client.images.search('golden gate bridge', { maxResults: 5 });
for (const img of images.imageResults ?? []) console.log(img.title, img.url);
await client.close();
}
main().catch(console.error);
Authentication
API Key
# Python
import os
from webiq import WebIQClient
client = WebIQClient(api_key=os.environ["WEBIQ_API_KEY"])
// TypeScript
import { WebIQClient } from '@microsoft/webiq';
const client = new WebIQClient({ apiKey: process.env.WEBIQ_API_KEY });
Microsoft Entra ID
Pass any Azure Identity TokenCredential.
# Python — uses `azure-identity`
from azure.identity import DefaultAzureCredential
from webiq import WebIQClient
client = WebIQClient(credential=DefaultAzureCredential())
// TypeScript — uses `@azure/identity`
import { DefaultAzureCredential } from '@azure/identity';
import { WebIQClient } from '@microsoft/webiq';
const client = new WebIQClient({
credential: new DefaultAzureCredential(),
});
SDK resources and methods
The client exposes five resource namespaces: web, videos, browse, news, and images.
Web Search
client.web.search() — Search the web and get page content for grounding.
response = client.web.search(
query="search terms", # Required, 1–1000 chars
max_results=10, # 1–50 (default 10)
language="en", # ISO 639-1 code
region="US", # Country/region code
location="lat:40.7;long:-74.0", # Optional
content_format=ContentFormat.passage, # passage (default) | text | html | markdown (full-page content)
max_length=3000, # Max content chars (default 3000)
)
# response.webResults → list of { title, url, content, lastUpdatedAt, crawledAt, language, isAdult }
Videos Search
client.videos.search() — Find relevant video content with optional playlist support.
response = client.videos.search(
query="search terms", # Required, 1–1000 chars
max_results=30, # 1–30 (default 30)
language="en",
region="US",
enable_playlist=True, # Include playlist results
freshness="month", # week | month | year
)
# response.videoResults → list of { title, url, description, length, viewCount, moments[], thumbnailUrl, ... }
# response.playlists → list of { title, videos[] }
Browse
client.browse.fetch() — Extract content from any URL. Supports live crawling and JavaScript rendering.
response = client.browse.fetch(
url="https://www.microsoft.com", # Required
max_length=10000, # Max content chars, 1–500000
live_crawl="fallback", # "none" (cached only, default) | "fallback" (crawl if not cached) | "force" (always crawl, bypass cache)
include_web_links=True, # Include outbound hyperlinks
include_image_links=True, # Include image URLs
render_dynamic_pages=False, # JS rendering (requires live_crawl="fallback")
content_format=BrowseContentFormat.markdown, # text | html | markdown
)
# response → { url, title, content, isAdult, lastUpdatedAt, crawledAt, retryAfter, traceId }
News Search
client.news.search() — Get the latest news articles with the passages most relevant to the query.
response = client.news.search(
query="search terms", # Required, 1–1000 chars
max_results=10, # 1–20 (default 10)
language="en",
region="US",
location="lat:40.7;long:-74.0", # Optional
content_format=ContentFormat.passage,
max_length=3000,
)
# response.newsResults → list of { title, url, content, source, lastUpdatedAt, thumbnail, isAdult }
Image Search
client.images.search() — Find images with filtering by aspect ratio, size, and safe search.
from webiq.types import ImageAspectRatio, ImageSize, SafeSearch
response = client.images.search(
query="search terms", # Required, 1–1000 chars
max_results=30, # 1–30 (default 30)
language="en",
region="US",
aspect_ratio=ImageAspectRatio.wide, # square | wide | tall
image_size=ImageSize.large, # small | medium | large | extraLarge
safe_search=SafeSearch.strict, # off | strict
watermark_free=True,
)
# response.imageResults → list of { title, url, hostPageUrl, caption, width, height, thumbnailUrl, ... }
Note
TypeScript methods use the same parameters in camelCase (e.g., maxResults, contentFormat, liveCrawl, aspectRatio). See the TypeScript Quick Start above for the calling convention.
Async Support
Python Support
import asyncio
from webiq import WebIQAsyncClient
async def main():
async with WebIQAsyncClient(api_key="your-api-key") as client:
web, news = await asyncio.gather(
client.web.search("async Python"),
client.news.search("programming"),
)
for r in web.webResults or []:
print(r.title)
asyncio.run(main())
TypeScript Support
TypeScript is natively async. Use Promise.all for concurrency:
const [web, news, videos] = await Promise.all([
client.web.search('TypeScript'),
client.news.search('programming'),
client.videos.search('tutorial'),
]);
Configuration
Timeout and Retry
The client accepts timeout and retry as top-level options. Per-call timeout on each resource method overrides the client-level default. The SDK auto-retries 408, 500, 502, 503, 504 with exponential backoff and jitter; if the response body includes a retryAfter field (e.g. "30s"), the SDK waits for that hint instead of the backoff delay. 429 and 430 are never auto-retried — they surface immediately as RateLimitError so your application can apply its own back-off policy. Client-side timeouts are also not retried (they raise APIConnectionError).
# Python
from webiq import WebIQClient, RetryPolicy
client = WebIQClient(
api_key="your-api-key",
timeout=10.0, # seconds (default: 10.0)
retry=RetryPolicy(
max_retries=2, # defaults shown
base_delay_s=0.25,
max_delay_s=4.0,
),
)
# Per-call override
response = client.web.search("query", language="de", region="DE", timeout=30.0)
// TypeScript
import { WebIQClient, RetryPolicy } from '@microsoft/webiq';
const client = new WebIQClient({
apiKey: 'your-api-key',
timeout: 10000, // milliseconds (default: 10000)
retry: new RetryPolicy({
maxRetries: 2, // defaults shown
baseDelaySeconds: 0.25,
maxDelaySeconds: 4.0,
}),
});
Customized HTTP client (proxy, TLS, ...)
For advanced HTTP settings — proxy, custom TLS, connection pooling, etc. The SDK does not close caller-owned clients.
# Python — pass a pre-configured `httpx.Client` via `http_client`
import httpx
from webiq import WebIQClient, WebIQAsyncClient
# sync
http_client = httpx.Client(
base_url="https://api.microsoft.ai/v3",
proxy="http://proxy:8080",
)
client = WebIQClient(api_key="your-api-key", http_client=http_client)
# async
http_client = httpx.AsyncClient(
base_url="https://api.microsoft.ai/v3",
proxy="http://proxy:8080",
)
client = WebIQAsyncClient(api_key="your-api-key", http_client=http_client)
// TypeScript — pass a custom `fetchFn` that wraps the global `fetch`
import { ProxyAgent } from 'undici';
import { WebIQClient } from '@microsoft/webiq';
const proxyAgent = new ProxyAgent('http://my-proxy:8080');
const client = new WebIQClient({
apiKey: 'your-api-key',
// Spread `init` so headers, body, and the SDK's internal `AbortSignal` are preserved.
fetchFn: (url, init) => fetch(url, { ...init, dispatcher: proxyAgent }),
});
Cancellation (TypeScript)
Every TypeScript resource method accepts an AbortSignal via options.signal. When the signal aborts, the in-flight request is cancelled, retries stop, and the signal's reason is re-thrown unchanged (no APIConnectionError wrapping).
// Apply a wall-clock budget that includes retries (the `timeout` option is per attempt only)
const result = await client.web.search('latest news', {
signal: AbortSignal.timeout(5_000),
});
// Combine signals (e.g. an inbound request signal plus a wall-clock cap)
const result2 = await client.web.search('latest news', {
signal: AbortSignal.any([req.signal, AbortSignal.timeout(5_000)]),
});
Telemetry Hook
Monitor every request/response for logging, metrics, or debugging.
# Python
from webiq import WebIQClient, TelemetryEvent
def on_request(event: TelemetryEvent):
print(f"{event.method} {event.path} → {event.status_code} ({event.elapsed_ms}ms)")
client = WebIQClient(api_key="your-api-key", telemetry_hook=on_request)
// TypeScript
import { WebIQClient, TelemetryEvent } from '@microsoft/webiq';
const client = new WebIQClient({
apiKey: 'your-api-key',
telemetryHook: (event: TelemetryEvent) => {
console.log(`${event.method} ${event.path} → ${event.statusCode} (${event.elapsedMs}ms)`);
},
});
The TelemetryEvent fields are:
- Python (snake_case):
method,path,transport,attempt,status_code,request_id,trace_id,retry_after,elapsed_ms,error,error_body - TypeScript (camelCase):
method,path,transport,attempt,statusCode,requestId,traceId,retryAfter,elapsedMs,error,errorBody
Error Handling
Both SDKs provide typed exceptions for structured error handling. All errors inherit from WebIQError.
| Exception | HTTP Status | Trigger |
|---|---|---|
AuthenticationError |
401 | Invalid or missing API key |
PermissionDeniedError |
403 | Authenticated but not authorized for the resource |
RateLimitError |
429, 430 | Rate limit / concurrent-request limit exceeded |
APIStatusError |
400, 404, 500, 503, 504, ... | All other HTTP errors |
APIConnectionError |
— | Network issues, DNS failures, timeouts |
WebIQError |
— | Base class for all SDK errors |
PermissionDeniedError and RateLimitError are both subclasses of APIStatusError, so a broader handler on APIStatusError still catches them. The SDK does not automatically retry 429 / 430 responses — the server's back-off hint is surfaced on error.retry_after (Python) / error.retryAfter (TypeScript) for your application to act on.
# Python
from webiq import (
WebIQError,
APIConnectionError,
APIStatusError,
AuthenticationError,
PermissionDeniedError,
RateLimitError,
)
try:
response = client.web.search("test")
except PermissionDeniedError as e:
# 403 — authenticated, but not allowed to call this resource
print(f"Forbidden (HTTP {e.status_code}): {e}")
except AuthenticationError as e:
# 401 — invalid or missing API key
print(f"Auth failed (HTTP {e.status_code}): {e}")
except RateLimitError as e:
# 429 or 430 — rate limit / concurrent-request limit (never auto-retried)
print(f"Rate limited. Retry after: {e.retry_after}")
except APIStatusError as e:
print(f"API error (HTTP {e.status_code}): {e}")
except APIConnectionError as e:
print(f"Connection failed: {e}")
except WebIQError as e:
print(f"SDK error: {e}")
// TypeScript
import {
WebIQError,
APIConnectionError,
APIStatusError,
AuthenticationError,
PermissionDeniedError,
RateLimitError,
} from '@microsoft/webiq';
try {
const results = await client.web.search('test');
} catch (error) {
if (error instanceof PermissionDeniedError) {
// 403 — authenticated, but not allowed to call this resource
console.error(`Forbidden (HTTP ${error.statusCode}):`, error.message);
} else if (error instanceof AuthenticationError) {
// 401 — invalid or missing API key
console.error(`Auth failed (HTTP ${error.statusCode}):`, error.message);
} else if (error instanceof RateLimitError) {
// 429 or 430 — rate limit / concurrent-request limit (never auto-retried)
console.error(`Rate limited. Retry after: ${error.retryAfter}`);
} else if (error instanceof APIStatusError) {
console.error(`API error (HTTP ${error.statusCode}):`, error.message);
} else if (error instanceof APIConnectionError) {
console.error('Connection failed:', error.message);
}
}
Inspecting Error Details
All APIStatusError instances (including PermissionDeniedError and RateLimitError) expose the full error response body. Most input-validation problems on the Python SDK (e.g. max_results out of range, empty query) are caught client-side by Pydantic and raise pydantic.ValidationError before any request is sent; the pattern below applies when the server rejects a request. browse.fetch is a convenient way to trigger one — a missing or filtered URL surfaces as a structured error:
# Python
from webiq import WebIQClient
from webiq.errors import APIStatusError
client = WebIQClient(api_key="your-api-key")
try:
response = client.browse.fetch("https://www.not-microsoft.com")
except APIStatusError as e:
print(f"Status: {e.status_code}") # e.g. 404
print(f"Message: {e}") # e.g. "No result is found"
if isinstance(e.body, dict):
print(f"Error code: {e.body.get('errorCode')}") # e.g. "BrowseApiDocNotFound"
print(f"Category: {e.body.get('errorCategory')}") # e.g. "UserError"
print(f"Details: {e.body.get('technicalDetails')}") # e.g. "NotFound"
print(f"Trace ID: {e.body.get('traceId')}")
print(f"Retry after: {e.body.get('retryAfter')}")
// TypeScript
import { WebIQClient, APIStatusError } from '@microsoft/webiq';
async function main() {
const client = new WebIQClient({ apiKey: 'your-api-key' });
try {
await client.browse.fetch('https://www.not-microsoft.com');
} catch (error) {
if (error instanceof APIStatusError) {
console.error(`Status: ${error.statusCode}`); // e.g. 404
console.error(`Message: ${error.message}`); // e.g. "No result is found"
// Full error body from the API response
if (error.body && typeof error.body === 'object') {
const body = error.body as Record<string, unknown>;
console.error(`Error code: ${body.errorCode}`); // e.g. "BrowseApiDocNotFound"
console.error(`Category: ${body.errorCategory}`); // e.g. "UserError"
console.error(`Details: ${body.technicalDetails}`); // e.g. "NotFound"
console.error(`Trace ID: ${body.traceId}`); // for debugging with support
console.error(`Retry after: ${body.retryAfter}`); // for retryable errors
}
}
}
}
main().catch(console.error);
Debugging Tips
traceId— every response includes atraceIdfield for end-to-end debugging. Include it when contacting support.- Telemetry hook — attach a callback to log every request attempt, status code, and timing.
- Error body —
APIStatusError.bodycontains the full server response, includingerrorCode,errorCategory,technicalDetails, andtraceId.