# UK Grants Scraper: Find a Grant + Innovate UK (`hustle_staxx/uk-grants-feed`) Actor

Open UK government grants in one clean dataset: Find a Grant and Innovate UK competitions, with amounts, deadlines and eligibility. Watch mode returns only new, changed or removed grants, so it is ideal for alerts. Not affiliated with GOV.UK, Innovate UK or UKRI.

- **URL**: https://apify.com/hustle_staxx/uk-grants-feed.md
- **Developed by:** [Hustle Staxx](https://apify.com/hustle_staxx) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 grant record delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK Government Grants Feed (Find a Grant + Innovate UK)

**Not affiliated with or endorsed by GOV.UK, Innovate UK or UKRI.**

One clean, deduplicated feed of UK public funding from two official GOV.UK services:

- **Find a Grant** (find-government-grants.service.gov.uk), the government's central grants list
- **Innovate UK** competitions (apply-for-innovation-funding.service.gov.uk)

Turn on **watch mode** and run it on a schedule, and you only get grants that are new, changed or no longer listed. That makes it easy to send alerts to Slack, email or a CRM.

### Why use this instead of searching Find a Grant yourself

Find a Grant is free and you can search it by hand. This feed is for when you want to check often, or pass grants to other tools:

- **One feed of two sources.** Find a Grant and Innovate UK competitions come back in the same format, with the same field names, deadlines as ISO dates and award sizes as numbers where the listing gives them.
- **Only what changed.** In watch mode each run reports just the grants that are new, changed (with the fields that changed) or no longer listed, so you do not have to re-read the whole list to spot what is different.
- **Filters.** Keep only the sources you want and only grants whose title or summary matches your keywords; closed grants are left out unless you ask for them.
- **Ready for your other tools.** Results land in an Apify dataset, so Apify's integrations (Slack, email, webhooks, Zapier, Make, Google Sheets and others) can send them to a channel, spreadsheet or CRM on every scheduled run.

### What you get

| Field | Meaning |
| --- | --- |
| `id`, `source`, `title`, `url` | The grant and its official page |
| `summary` | The short description from the listing |
| `funder` | Funding organisation |
| `location`, `whoCanApply` | Lists, as shown on the listing |
| `amountText`, `amountMin`, `amountMax` | Award size, as text and as numbers in pounds (Find a Grant) |
| `totalFundText`, `totalFund` | Total size of the scheme |
| `opensOn`, `closesOn` | ISO dates |
| `status` | `open`, `upcoming` or `closed` |
| `change` | `new`, `changed` or `removed` in watch mode, `listed` otherwise |
| `changedFields` | Which fields changed since the last run |
| `fetchedAt` | UTC time of the run |

Innovate UK states amounts only in free text, so the numeric amount fields stay empty for its competitions rather than being guessed.

### Example input

```json
{
  "sources": ["find-a-grant", "innovate-uk"],
  "watch": true,
  "includeClosed": false,
  "keywords": ["energy", "net zero", "community"],
  "maxPagesPerSource": 30,
  "delaySeconds": 2
}
```

### Example output item

A made-up grant, to show the shape of one record:

```json
{
  "id": "find-a-grant:example-community-energy-pilot",
  "source": "find-a-grant",
  "title": "Community Energy Pilot (example)",
  "url": "https://www.find-government-grants.service.gov.uk/grants/example-community-energy-pilot",
  "summary": "Funding for community groups to plan small local energy projects.",
  "funder": "Example Department",
  "location": ["England"],
  "whoCanApply": ["Non-profit", "Public Sector"],
  "amountText": "From £5,000 to £50,000",
  "amountMin": 5000,
  "amountMax": 50000,
  "totalFundText": "£2 million",
  "totalFund": 2000000,
  "opensOn": "2026-10-01",
  "closesOn": "2026-12-15",
  "status": "open",
  "change": "changed",
  "changedFields": ["closesOn"],
  "fetchedAt": "2026-10-15T07:00:05+00:00"
}
```

### Filters

- **`sources`**: `find-a-grant`, `innovate-uk`, or both (the default).
- **`keywords`**: keeps a grant if its title or summary contains any of the words or phrases, ignoring case. Words match inside longer words, so `ai` also matches "maintain"; use longer words or phrases (`artificial intelligence`) to avoid that. Location, funder and eligibility are not searched; filter on those fields in your own tool.
- **`includeClosed`**: off by default, so grants past their closing date are left out.
- **`watch`**: off by default (every run returns the full filtered list, marked `listed`). Turn it on for scheduled runs to get only `new`, `changed` and `removed` grants.

Watch mode remembers each combination of sources, keywords and `includeClosed` separately, so changing a filter starts a fresh history and the next run reports every matching grant as new.

### How it reads the sites

- Reads `robots.txt` first and always obeys it.
- Reads listing pages only, one request at a time, with at least one second between requests.
- Never collects contact names, emails or phone numbers.
- If a source cannot be read in full, it is reported in the run status and its previous results are kept. Nothing is wrongly marked as removed.

### Licence

Content from both services is Crown copyright and published under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). When you reuse it, keep the attribution: "Contains public sector information licensed under the Open Government Licence v3.0." UKRI's own funding finder is not included, because its content is licensed for non-commercial use only.

### For site owners

This feed identifies itself as `HustleStaxxGrantsFeed` and obeys robots.txt. To block it, add this to your robots.txt:

```
User-agent: HustleStaxxGrantsFeed
Disallow: /
```

### Pricing

You pay per event, in US dollars:

| Event | Price | When |
| --- | --- | --- |
| Run start | $0.02 | Once per run |
| Grant record | $0.025 | Each record written to the dataset, including `removed` records in watch mode |

Grants that are filtered out, or unchanged in watch mode, cost nothing.

**Worked examples**

- A run that returns 50 grants: $0.02 + 50 × $0.025 = **$1.27**.
- A quiet watch-mode day with nothing new: **$0.02**. With 3 new or changed grants: $0.02 + 3 × $0.025 = **$0.095**.
- A full list with no keywords can run to a few hundred grants (up to about 300 per source with the default `maxPagesPerSource`); 300 grants would cost $0.02 + 300 × $0.025 = $7.52. Keywords and watch mode keep this down.

Set a maximum cost per run in the Apify Console; the feed stops cleanly when it is reached and the run ends as a success with the records delivered so far.

### Limits

- Two sources only: Find a Grant and Innovate UK competitions. UKRI's funding finder is not included (see Licence).
- Reads the listing pages, not each grant's detail page, so fields are what the listing shows. Innovate UK listings give amounts only in free text, so its numeric amount fields stay empty.
- Up to 100 listing pages per source (`maxPagesPerSource`, default 30; about 10 grants per page).
- One request at a time, at least 1 second apart (default 2 seconds, longer if robots.txt asks).
- Always check the official grant page before applying; the listing can change after a run.

### Troubleshooting

- **A source is reported as skipped in the status message** (for example `innovate-uk skipped: page 2 returned 503`). That source could not be read in full this time, so its records are left out of this run and its previous watch-mode history is kept: nothing from it is wrongly reported as removed. The other source is still delivered and the run ends as a success. Run again later. If every selected source fails, the run fails.
- **Checking a run automatically (schedules, webhooks).** Every run writes a `RUN_SUMMARY` record to its default key-value store, for example:
  ```json
  {"delivered": 2, "counts": {"listed": 2}, "sourcesRead": ["innovate-uk"], "sourcesFailed": [{"source": "find-a-grant", "reason": "robots.txt does not allow the listing pages"}], "stopReason": null}
  ```
  If `sourcesFailed` is not empty, that run is partial: read it before treating missing grants as gone.
- **"no listings found on the first page; the page layout may have changed".** The site's page layout has probably changed and the feed needs an update. The run keeps your previous history, so nothing is lost; please report it through the Issues tab.
- **"stopped at maxPagesPerSource (30) before the last page".** The source has more listing pages than the limit. Raise `maxPagesPerSource`, or narrow the results with keywords (the pages are still read in full, but fewer records are charged).
- **"robots.txt does not allow the listing pages".** The site has asked crawlers not to read those pages, and the feed obeys.
- **The dataset is empty but the run succeeded.** In watch mode this means nothing changed since the last run. Without watch mode, your keywords matched no open grants; try broader words.
- **Watch mode reported everything as new.** It is the first run for that combination of filters (see Filters).

### Change log

#### 0.2 (2026-10-15)

- README: why use this instead of searching Find a Grant yourself, prices in money with worked examples, example input and output, a filter guide, limits, troubleshooting and this change log.
- Every run writes a machine-readable `RUN_SUMMARY` (records delivered, sources read, sources that failed and why), so scheduled runs and webhooks can spot a partial run.
- The User-Agent now reports version 0.2.

#### 0.1 updates (2026-10-14)

- Watch mode is now off by default, so a run on default input always returns the current list. Turn it on for scheduled alerts.
- Added output and dataset schemas, so the Output tab shows an overview table.

#### 0.1 (2026-10-14)

- First release.

### Local development

```bash
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt pytest
python -m pytest -q   # uses fake pages, no network needed
```

# Actor input Schema

## `sources` (type: `array`):

Which GOV.UK services to read.

## `watch` (type: `boolean`):

Remembers the last run and only outputs grants that are new, changed or no longer listed. Turn on for scheduled alerts. Off returns the full list every time.

## `includeClosed` (type: `boolean`):

Find a Grant still lists some schemes after their closing date. Off by default.

## `keywords` (type: `array`):

Only keep grants whose title or summary contains any of these words. Leave empty for everything.

## `maxPagesPerSource` (type: `integer`):

Each page holds about 10 grants. If a source has more pages than this, it is reported as incomplete.

## `delaySeconds` (type: `number`):

Polite pause between page requests. A longer Crawl-delay in robots.txt always wins.

## Actor input object example

```json
{
  "sources": [
    "find-a-grant",
    "innovate-uk"
  ],
  "watch": false,
  "includeClosed": false,
  "keywords": [],
  "maxPagesPerSource": 30,
  "delaySeconds": 2
}
```

# Actor output Schema

## `results` (type: `string`):

One record per grant: title, funder, amounts, dates, status and, in watch mode, what changed.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("hustle_staxx/uk-grants-feed").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("hustle_staxx/uk-grants-feed").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call hustle_staxx/uk-grants-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hustle_staxx/uk-grants-feed"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hrwNeVxLYdopPTkBZ/builds/nHuL7TD3ZKlyY7EZu/openapi.json
