OData feed for BI tools and data warehouses
Read ten of your organisation's record types into Power BI, Excel or a data warehouse through a standard OData v4 feed, with filters, paging and change tracking.
Last updated
The OData feed is a read-only address that business intelligence tools understand without any
custom code. Power BI, Excel and most data warehouse loaders can read it: they ask the feed what it
holds, then read each table page by page. It lives on your organisation's own address, under
/odata/v4/.
The feed reads the same records your organisation's reports read, and only your own organisation's. Backed sends nothing anywhere: your tool asks, and the feed answers with the records as they are at that moment.
Before you start
- An Administrator creates an API credential under
Settings > Profile and terms, on theAPI Accesstab, as API access explains, and ticksRead reporting feedunderScopes. Only an Administrator who also holdsCan generate reportscan give that scope. You can add it to an existing credential withEdit scopes; it takes effect on the credential's next request, with no new token needed. - Tick
Include bank account numbersonly if the tool needs applicants' full bank account numbers. Without it, the feed leaves those columns out. - Keep the credential's
Client IDandClient Secretto hand. The secret is shown once. - Know your organisation's address: the one your team signs in at, such as
https://your-subdomain.backed.giving. The feed is at that address plus/odata/v4/.
How the feed signs in
Every request carries a token in an Authorization: Bearer <token> header. The tool gets the token
by sending the credential's Client ID and Client Secret to /oauth/token on your
organisation's address, exactly as in Reporting feed for analytics tools. The feed
takes the token only from that header, never from the address or a cookie.
A tool that can only ask for a user name and password, such as Tableau's or Power BI's OData
connector, can sign in to the feed with the credential itself: the Client ID as the user name and
the Client Secret as the password. The feed checks the pair on every request, just as
/oauth/token checks it, so there is no token to renew. Only the feed takes a user name and
password; the rest of the API takes a token alone. A wrong pair, or a deleted credential, answers
401 Unauthorized.
A token lasts five days. After that, every request answers 401 Unauthorized until the tool asks
for a new one. A scheduled refresh needs a fresh token: either the query asks for one each time
it runs (the Power BI sample below does), or someone renews the token before it expires.
A credential without Read reporting feed answers 403 with insufficient_scope. A credential may
make 120 calls a minute, the feed and the rest of the API together; past that the feed answers 429
with a Retry-After header.
What the feed holds
| Entity set | What each row is |
|---|---|
Applications |
an Application, with a column for each of your Field Tags |
Applicants |
an applicant |
RegistrationEntities |
an applicant organisation or person |
FundingRounds |
a Funding Round |
Assessments |
an assessment |
Decisions |
a decision line |
Contracts |
a contract |
Milestones |
a milestone, with a column for each of your Field Tags on its deliverables |
Payments |
a payment |
Funds |
a Fund |
The columns are those of the matching data source in a Backed report, so a column in your tool and
a column in a data table match by name. Every set
also has an updated_at column. The feed never carries people's email addresses, phone numbers,
postal addresses or first and last names, whatever the credential.
/odata/v4/ lists the ten entity sets, and /odata/v4/$metadata describes every column and its
type, which is how a BI tool builds its tables. The API's OpenAPI document points to the feed but
does not describe it: $metadata does. See Developer documentation.
Field Tag columns
On Applications and Milestones, each of your organisation's Field Tags is a text column named
after the tag, such as amount_requested. A character a column name can't hold becomes _, and a
name that would repeat another column's gets the tag's number added, such as title_42. A table or
repeatable answer comes back as one value, its cells separated by commas. Tag the fields you report
on, and the columns keep their names across Funding Rounds while the tags keep theirs.
Value formats
Amounts are numbers. Dates are YYYY-MM-DD, and dates with times are in UTC, such as
2026-11-08T03:15:00.000Z.
Ask for less: query options
Add these to an entity set's address to narrow what comes back:
| Option | Example | What it does |
|---|---|---|
$select |
$select=record_id,application_title,current_status |
only these columns |
$filter |
$filter=grant_id eq 12 and submission_date ge 2026-07-01T00:00:00Z |
only rows that match |
$orderby |
$orderby=submission_date desc |
sort by up to eight columns, each asc or desc |
$top |
$top=200 |
at most this many rows in all |
$count |
$count=true |
adds @odata.count, the number of rows that match |
A filter compares one column with one value using eq, ne, gt, ge, lt or le, and joins
comparisons with and, or, not and brackets. Text goes in single quotes, and dates are written
as 2026-07-01 or 2026-07-01T00:00:00Z. A list or JSON column can't be filtered or sorted.
The feed answers 501 with not_implemented for an option it doesn't support, such as $skip,
$expand, $search or $apply, and for a filter that uses a function (contains,
startswith), arithmetic, in or has. A mistake, such as a column that doesn't exist or the same
option twice, answers 400 with bad_request. Both say which option was wrong. An entity set name
is case-sensitive; one that doesn't exist answers 404.
Pages
A page holds up to 1,000 rows. When there are more, the page ends with @odata.nextLink: the
address of the next page, with your options carried in it. Read that address next, and stop when a
page has no @odata.nextLink. Power BI and Excel do this for you. A $top larger than 1,000 is not
refused: you get 1,000 rows and a @odata.nextLink for the rest. Use the feed's own links rather
than $skip, which the feed does not support.
Read only what changed
A warehouse loader that refreshes often can read only the rows changed since its last read, instead of the whole set:
- Read an entity set in full with the header
Prefer: odata.track-changes. Follow every@odata.nextLinkas usual. Leave out$filter: delta links are issued for unfiltered reads only (a filtered read is answered without one, and a delta link read with a$filteranswers400), so apply any filter on your side. - The last page ends with
@odata.deltaLinkinstead. Keep that address. - Next time, read the
@odata.deltaLink. It returns the rows changed since step 1, and its last page ends with a new@odata.deltaLinkfor the time after that.
A changed row comes back whole. A deleted assessment, payment or Fund comes back as a deleted-entity
entry, in OData 4.0's form: "@odata.context" ending $metadata#<EntitySet>/$deletedEntity, an
"id" such as "Payments(123)", "reason": "deleted" and the record's key, on the first page of
the delta read; the entry is dated from when the feed
recorded the deletion, a few seconds after it happened, so a delta read in that gap still gets it
next time. The other entity sets carry no
deleted-entity entries: most of their records are never deleted (they close, withdraw or decline, and
reach you as a changed row), but an Administrator can delete an application or a registration
entity, and such a record simply stops appearing without a removal entry. If your model must drop
deleted applications or organisations, schedule a full reload of Applications, Applicants and
RegistrationEntities (weekly is usual) beside the incremental refresh of the rest.
A delta link works only for the credential it was issued to, and only for 30 days after the read
that issued it. An older, unknown or another credential's delta link answers 410 with
delta_token_expired: read the entity set in full again, with Prefer: odata.track-changes, and
start over from the new link.
Power BI and Excel don't use delta links: each refresh reads the tables in full.
Load the feed into Power BI Desktop
The simplest route is Power BI's own OData feed connector, signing in with the credential:
- In Power BI Desktop, select
Get data, thenOData feed. - Under
URL, enter your organisation's address plus/odata/v4/, such ashttps://your-subdomain.backed.giving/odata/v4/, and selectOK. - When Power BI asks how to connect, choose
Basic. Enter theClient IDas theUser nameand theClient Secretas thePassword, and selectConnect. - In the
Navigator, tick the entity sets you want, then selectLoadorTransform Data.
Power BI keeps the pair with its data source settings, not in the .pbix file. If you would rather
the feed see a token, start from a blank query that asks for one and then opens the feed:
- In Power BI Desktop, select
Get data, thenBlank query. - In the Power Query editor, select
Advanced Editor, paste the sample below, and replace the three settings at the top with your organisation's address,Client IDandClient Secret. - Select
Done. The query lists the ten entity sets; selectTablebeside each one you want. - If Power BI asks how to connect to your organisation's address, choose
Anonymous: the query sends its own token. - Set column types where you need to, then select
Close & Apply.
let
// Your organisation's address and your credential's pair.
BaseUrl = "https://your-subdomain.backed.giving",
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
// A token, asked for at every refresh (each lasts five days).
Token = Json.Document(
Web.Contents(BaseUrl, [
RelativePath = "oauth/token",
Headers = [#"Content-Type" = "application/x-www-form-urlencoded"],
Content = Text.ToBinary(Uri.BuildQueryString([
grant_type = "client_credentials",
client_id = ClientId,
client_secret = ClientSecret
]))
])
)[access_token],
// The feed, with the token on every request it makes.
Feed = OData.Feed(
BaseUrl & "/odata/v4/",
null,
[Headers = [Authorization = "Bearer " & Token], Implementation = "2.0"]
)
in
Feed
Filters and column choices you make in Power Query are sent to the feed as $filter and
$select, so only what you keep is read. A step the feed can't answer, such as Remove Top Rows
(sent as $skip) or a text filter like Begins With (sent as startswith), fails with 501. Put
such a step after a Table.Buffer step, so Power BI applies it to rows it has already read.
Refresh on a schedule
- In Power BI Desktop, select
Publishand choose a workspace. - In the Power BI service, open the semantic model's
Settings. - Under
Data source credentials, selectEdit credentialsfor your organisation's address. For a model built with theOData feedconnector, chooseBasicand enter theClient IDandClient Secretagain; for one built with the sample query, chooseAnonymous. - Under
Refresh, turn onConfigure a refresh schedule, choose the times, and selectApply.
With Basic, every refresh signs in with the pair; the sample asks for a new token at every
refresh. Either way, the five-day token limit never stops it. If a
refresh fails with 401 Unauthorized, the credential has been deleted or its secret is wrong; with
403, it has lost Read reporting feed. Keep the .pbix file to people who may see the
credential's secret, as it is written in the query.
Load the feed into Excel
Excel's Get Data > From Other Sources > From OData Feed signs in as Power BI's OData feed
connector does: choose Basic and enter the Client ID and Client Secret. To send a token
instead, use the same query as Power BI:
- In Excel, select
Data, thenGet Data,From Other Sources,Blank Query. - Select
Advanced Editor, paste the Power BI sample above, and replace the three settings. - Select
Done, chooseAnonymousif Excel asks how to connect, pick the entity sets you want, then selectClose & Load.
Refresh All on the Data tab asks for a new token each time.
Tableau and other tools
Tableau Desktop reads the feed through its OData connector, signing in with the credential:
- In Tableau Desktop, under
Connect, selectTo a Server, thenOData. - Under
Server, enter your organisation's address plus/odata/v4/, such ashttps://your-subdomain.backed.giving/odata/v4/. - Under
Authentication, chooseUsername and Password. Enter theClient IDas theUsernameand theClient Secretas thePassword, and selectSign In. - Pick the entity set you want, such as
Applications, and build your view. Add another OData connection for each further entity set.
Any other tool that can send an Authorization header with each request can read the feed, with a
token or with the credential's pair as a user name and password.
A loader that runs on a schedule should ask for a new token at each run, keep each entity set's
@odata.deltaLink, and read in full again when a delta link answers 410.
Who read what
Every read of an entity set is written to your organisation's Audit log as reporting_feed.read,
with the entity set, the credential and the number of rows. Reading the list of entity sets or
$metadata is not recorded, as neither holds any of your records.
See also
This page uses Backed's default terms, such as Funding Round and Application Template. An organisation may rename them, so read each as your organisation's own term if you've renamed it.
Was this helpful?