Web.Contents

Accessing Data

Downloads content from a URL as binary.

Examples on this page use shared sample tables. View them to understand the input data before reading the examples below.

Syntax

Web.Contents(url as text, optional options as nullable record) as binary

Parameters

NameTypeRequiredDescription
urltextYesThe URL to download content from.
optionsrecordNoAn options record controlling headers, query parameters, request method, and more.

Return Value

binaryThe binary content downloaded from the URL.

Remarks

Web.Contents fetches content from a URL and returns it as a binary value. You then pipe the result to a parser such as Json.Document, Csv.Document, or Xml.Document to convert it into a usable M value.

Options record fields

The optional options record supports the following fields:

FieldTypeDescription
QueryrecordAppend query-string parameters to the URL. e.g., [Query = [page = "1", size = "50"]]
HeadersrecordHTTP headers to include. e.g., [Headers = [Authorization = "Bearer token123"]]
ContentbinarySupplying this field changes the request from GET to POST. The value becomes the POST body.
RelativePathtextA path segment appended to the base URL. Useful for dynamic endpoint construction.
TimeoutdurationOverride the default request timeout. e.g., #duration(0, 0, 30, 0) for 30 seconds.
ManualStatusHandlinglistA list of HTTP status codes (e.g., {404, 500}) that should not raise an error, allowing you to handle them in M code.
IsRetrylogicalSet to true to ignore any cached response and re-fetch from the server.
ExcludedFromCacheKeylistHeader names that should not be included in the cache key (useful for volatile auth headers).
ManualCredentialslogicalSet to true to bypass Power Query's credential prompt when you handle authentication yourself via Headers.
ApiKeyNametextName of the API key parameter when using the Web API credential type.

Important notes

  • Web.Contents breaks query folding — any transformations applied after it run entirely in the M engine.
  • In the Power BI Service, the base URL must be a static string literal for data source credentials to bind correctly. Use RelativePath and Query for dynamic parts. Power Query automatically inserts a / between the base URL and RelativePath — don't include a leading slash yourself.
  • Adding any Content field — even an empty binary — converts the request from GET to POST. Use Uri.BuildQueryString to build form-encoded POST bodies from a record instead of manually concatenating strings.
  • Queries that combine a POST request (e.g. fetching an OAuth token) and a GET request in the same query may fail to refresh in the Power BI Service due to dynamic data source detection. Consider using Dataflows, a Data Gateway with a custom connector, or splitting the token fetch into a separate query.
  • For paginated APIs, combine Web.Contents with List.Generate or a recursive function to fetch multiple pages.

Typical usage patterns

GET request with query parameters:

let
    Source = Web.Contents("https://api.example.com/data", [
        Query = [category = "electronics", page = "1"],
        Headers = [Accept = "application/json"]
    ]),
    Parsed = Json.Document(Source)
in
    Parsed

POST request with a JSON body:

let
    Body = Json.FromValue([name = "Test", value = 42]),
    Source = Web.Contents("https://api.example.com/items", [
        Content = Body,
        Headers = [#"Content-Type" = "application/json"]
    ]),
    Parsed = Json.Document(Source)
in
    Parsed

Handling specific HTTP status codes manually:

let
    Response = Web.Contents("https://api.example.com/item/999", [
        ManualStatusHandling = {404}
    ]),
    Status = Value.Metadata(Response)[Response.Status],
    Result = if Status = 404 then "Not Found" else Json.Document(Response)
in
    Result

Examples

Example 1: Fetch JSON from a REST API

let
    Response = Web.Contents("https://pqm.guide/api/tables/Products"),
    Parsed   = Json.Document(Response),
    AsTable  = Table.FromRecords(Parsed[rows])
in
    AsTable
Applied Steps

The final output — a five-row table built from the rows field of the parsed JSON using Table.FromRecords.

ProductID
ProductName
Category
Price
InStock
11Widget AWidgets25TRUE
22Gadget BGadgets50TRUE
33Widget CWidgets15FALSE
44Gadget DGadgets75TRUE
55Thingamajig EMisc120FALSE

Example 2: Use RelativePath for Power BI Service compatibility

let
    TableName = "Sales",
    Response  = Web.Contents("https://pqm.guide", [
        RelativePath = "api/tables/" & TableName
    ]),
    Parsed  = Json.Document(Response),
    AsTable = Table.FromRecords(Parsed[rows])
in
    AsTable
Output
OrderID
CustomerName
Product
Category
UnitPrice
Quantity
OrderDate
Region
11AliceWidget AWidgets2541/15/2024East
22BobGadget BGadgets5021/18/2024West
33CharlieWidget CWidgets15102/1/2024East
44AliceGadget DGadgets7512/10/2024North
55DianaWidget AWidgets2563/5/2024West
66BobThingamajig EMisc12013/12/2024East
77CharlieGadget BGadgets5034/1/2024West
88DianaWidget CWidgets1584/15/2024North

Example 3: Handle a 404 with ManualStatusHandling

let
    Response = Web.Contents("https://pqm.guide/api/tables/NonExistent", [
        ManualStatusHandling = {404}
    ]),
    Status = Value.Metadata(Response)[Response.Status],
    Result =
        if Status = 404 then #table({"Status", "Message"}, {{404, "Table not found"}})
        else Table.FromRecords(Json.Document(Response)[rows])
in
    Result
Applied Steps

The final output — since the status is 404, returns a one-row table with the status code and a user-friendly message instead of failing.

Status
Message
1404Table not found

Compatibility

Power BI Desktop Power BI Service Excel Desktop Excel Online Dataflows Fabric Notebooks
Contributors
kyleamueller