# Kickstarter Projects Scraper (`dami_studio/kickstarter-projects-scraper`) Actor

Search Kickstarter and get one row per project: goal, pledged, backers, deadline, state, creator, category and location. Filter by category, place, funding progress or keyword. A setting Kickstarter would quietly ignore stops the run instead of billing you for the wrong projects.

- **URL**: https://apify.com/dami\_studio/kickstarter-projects-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Business, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.92 / 1,000 project scrapeds

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

## Kickstarter Projects Scraper

Search Kickstarter the way its own discover page does, and get a flat table back: what each project asked for, what it actually raised, how many people backed it, when it closes, who made it and where they are. No account, no cookies, no browser.

- Filter by state, category, location, search term, funding progress, goal size and amount raised.
- Money arrives twice: in the project's own currency and converted to US dollars, so an AUD campaign and a USD one sort against each other correctly.
- Paste a URL from kickstarter.com/discover and it runs that exact search.
- A setting Kickstarter would quietly ignore stops the run instead, so you never pay for the wrong projects.
- Runs with empty input return a labelled sample row, free, so you can see the shape first.

### Price

**$0.92 per 1,000 projects**, plus a **$0.0005 start fee per run**.

This is a flat rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.

| Projects | Total cost |
|---|---|
| 100 | $0.0925 |
| 1,000 | $0.9205 |
| 10,000 | $9.2005 |
| 100,000 | $92.0005 |

#### What is actually charged

- **One `project-scraped` event per project row written to the dataset.** Nothing else is metered per row.
- **Free:** the sample row an empty run returns, and every diagnostic row: a blocked target, a dead URL, a search that matched nothing. Those rows all carry `"charged": false`.
- Projects Kickstarter returns that do not actually match your filters are dropped before they are charged.
- Duplicates across your search terms are dropped before they are charged.
- A run stopped because a setting was not recognised charges nothing at all.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row.

### Input

```json
{
  "state": "live",
  "category": "games/tabletop games",
  "fundingStatus": "funded",
  "sortBy": "most_backed",
  "maxItems": 100
}
```

| Field | What it does |
|---|---|
| `state` | live, successful, failed, canceled or upcoming. Leave it empty to search every state at once. British "cancelled" is understood and corrected. |
| `category` | A section (`games`, `technology`, `design`) or a full path (`games/tabletop games`). A category id works too. There are 15 sections and 154 sub-categories. |
| `searchTerms` | Words to look for. Up to 20 per run, searched in turn. Kickstarter searches the whole campaign page, not just the title. |
| `location` | A place written the way Kickstarter writes it: `Berlin, Germany`, `London, UK`, `California`, `United Kingdom`. The name is looked up first and every row is checked against it. |
| `sortBy` | magic, popularity, newest, end\_date, most\_funded or most\_backed. Deep paging stops at 9,600 projects per search, so the sort decides which 9,600 you get. |
| `fundingStatus` | under\_75, 75\_to\_100 or funded. |
| `goalRange` | under\_1k, 1k\_to\_10k, 10k\_to\_100k, 100k\_to\_1m or over\_1m, on the goal converted to US dollars. |
| `pledgedRange` | The same five bands, applied to what the project actually raised. |
| `staffPicksOnly` | Only projects Kickstarter's own team has picked out. |
| `searchUrls` | Paste the address bar from a search on kickstarter.com/discover. Every setting in it is checked the same way a typed one is. |
| `maxItems` | Total projects across everything you asked for, shared evenly between your terms and URLs. Default 100, hard ceiling 5,000. Keep it low while testing. You pay per row. |
| `proxyUrls` | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as `http://user:pass@host:port`. |

Run it with **empty input** and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.

### Output

One row per project. A real row from a real run:

```json
{
  "ok": true,
  "charged": true,
  "recordType": "project",
  "searchLabel": "live projects in games/tabletop games",
  "projectId": "1129359461",
  "name": "Good Society: A Jane Austen RPG – 2nd Edition",
  "blurb": "Navigate the pitfalls of romance, reputation, manners, and marriage in the sumptuous new edition of this award-winning regency rpg.",
  "creatorName": "Storybrewers Roleplaying",
  "categoryName": "Tabletop Games",
  "categorySlug": "games/tabletop games",
  "categoryId": 34,
  "state": "live",
  "goal": 20000,
  "pledged": 166508,
  "currency": "AUD",
  "goalUsd": 14430.58,
  "pledgedUsd": 120140.32,
  "percentFunded": 832.54,
  "backersCount": 1023,
  "deadline": "2026-10-09T03:00:00.000Z",
  "launchedAt": "2026-09-09T20:00:01.000Z",
  "createdAt": "2026-02-04T06:11:31.000Z",
  "stateChangedAt": "2026-09-09T20:00:05.000Z",
  "projectUrl": "https://www.kickstarter.com/projects/storybrewers/good-society-a-jane-austen-rpg-2nd-edition",
  "country": "AU",
  "locationName": "Sydney, AU",
  "staffPick": true,
  "isStaffSpotlight": false,
  "hasVideo": true,
  "imageUrl": "https://i.kickstarter.com/assets/054/517/465/bc8561a110c2f09da066c9d1f7762e6e_original.jpg",
  "creatorUrl": "https://www.kickstarter.com/profile/storybrewers",
  "resultPage": 1,
  "totalMatches": 437,
  "scrapedAt": "2026-09-20T09:52:43.077Z"
}
```

#### Field notes

- `goal / pledged`: in the project's own currency, whatever `currency` says. A campaign run in AUD reports AUD here.
- `goalUsd / pledgedUsd`: the same two figures converted to US dollars using the rate Kickstarter itself applies. Use these to compare projects across currencies. The filters work on these too, so a 100,000 JPY goal counts as about $626.
- `percentFunded`: pledged as a percentage of goal. It goes well past 100. The row above reads 832.54, and five-figure percentages exist.
- `backersCount`: people, not pledges, at the moment the row was read. It keeps moving on a live project.
- `deadline`: when funding closes, ISO 8601 in UTC. On a project that has already closed this is when it closed.
- `state`: live, successful, failed or canceled. An upcoming project reads `submitted` or `started` instead, because that is what Kickstarter calls it before launch.
- `categorySlug`: the full path, like `games/tabletop games`. Split it on the slash to get the section.
- `locationName`: where the creator says they are, as Kickstarter displays it. It is not always the same as `country`, which is the project's registered country.
- `projectId`: Kickstarter's own id. Stable, and safe as a primary key when you re-run.

Every real row carries `"charged": true`. Sample rows carry `"_sample": true` and diagnostic rows carry `"_diagnostic": true` with an `errorCode` you can filter on, and neither is ever billed.

### How it works

- It asks Kickstarter's own discovery search for JSON and reads the fields straight out of it. No page rendering, no headless browser, no login, no cookie.
- Every filter value is checked against a list read off Kickstarter itself before a request goes out. This matters more than it sounds: Kickstarter answers a setting it does not recognise by dropping it and returning everything, at a perfectly normal HTTP 200. A run that did that would hand you 680,000 unfiltered projects and bill you for them.
- Each page of results is then checked again on the way out. Kickstarter's sub-category ranking mixes in neighbouring and unrelated projects, measured at about one row in six, and those are dropped before anything is charged.
- Rate limits on the target are handled automatically.
- Results are paged 48 at a time and pushed as they arrive, so a big run never builds up in memory.

### What people use it for

- Watching a category you sell into. Run `games/tabletop games` daily, diff on `projectId`, and you have every new campaign the morning it launches.
- Working out what a realistic goal looks like before launching your own: pull a few hundred funded projects in your category and look at the spread of `goalUsd` against `backersCount`.
- Finding creators to approach, by location and category, with their project page and profile link already in the row.
- Post-mortems on what did not work: `state: failed` with a goal range tells you a lot more than the success stories do.
- Tracking a whole category over months. The rows are flat and typed, so they load straight into a spreadsheet or a database without cleaning.
- Spotting projects about to close: sort by `end_date`, filter on `75_to_100`, and you have every campaign in the last stretch of its funding.

### Reading the output

Every run writes three kinds of row, and they are easy to tell apart:

- **Real rows** carry `"charged": true` and `"recordType": "project"`. One billed event each.
- **The sample row** carries `"_sample": true` and `"charged": false`. There is exactly one, it only appears when the input had nothing to search for, and it exists so you can look at the output shape before you spend anything.
- **Diagnostic rows** carry `"_diagnostic": true`, `"charged": false` and an `errorCode` you can switch on: `BAD_INPUT` when a setting was not recognised, `NO_RESULTS` when a search matched nothing or hit the paging wall, `NETWORK` when Kickstarter could not be reached, `RATE_LIMITED` when it throttled the run, `TIME_BUDGET` when the run ran out of time before reaching a search.

If you only want the data, filter on `charged == true`. The count of those rows always equals the number of events you were billed for, so the dataset is its own invoice.

### What happens when you get a setting wrong

Kickstarter does not reject a filter value it does not understand. It drops the filter and answers normally. Same HTTP 200, same JSON, just the whole catalogue instead of what you asked for. One letter is enough: `cancelled` returns every project in every state, while `canceled` returns the 67,000 that were actually cancelled.

So this Actor checks each value first and **stops the run** if one does not match, rather than running the wrong search and charging you for it. You get a free `BAD_INPUT` row naming the setting and what it accepts. The British spelling of "cancelled" is corrected rather than refused, because that one is unambiguous.

### Limitations

- Kickstarter hands over at most 9,600 projects per search, whatever the match count says. That is 200 pages of 48. Past that you have to narrow the search with a category, a location or a funding range. When a run reaches that wall it says so in a free diagnostic row rather than quietly stopping.
- This returns the project listing, not the campaign page. There is no campaign body, no reward tiers, no updates, no comments and no backer list.
- Upcoming projects have no funding data at all: no goal, no pledged total, no backer count, no deadline. Those fields come back null rather than as zeroes that would read as real figures.
- Figures are a snapshot at read time. A live project's pledged total and backer count will have moved by the time you look.
- Filtering by a sub-category is approximate on Kickstarter's side . It mixes in neighbouring and unrelated projects at roughly one row in six. Those are dropped and not charged, so you get fewer rows rather than wrong ones, and a search for a thin sub-category can come back smaller than its match count suggests.
- Search terms are not re-checked against the rows, because Kickstarter searches the whole campaign page and the words may not appear in the name or the blurb. Every other filter is checked.
- A location typed loosely is matched to the nearest place Kickstarter knows, and you are told which one it used. A bare woe\_id number is passed through as given and the rows cannot be checked against it.
- Creator details are limited to the public display name and profile link. No email, no phone, no address.
- The category list is read from Kickstarter and baked in. If Kickstarter adds a category, it will be refused here until this Actor is updated. That is the deliberate trade against silently returning everything.
- The hard ceiling is 5,000 rows per run and 20 search terms per run. For more, split the work across runs.

### Questions

**Can I get the campaign text, rewards or backer list?**

No. This reads the project listing: the numbers, the dates, the category, the creator and the links. The campaign body, reward tiers, updates, comments and backers are not in it.

**Why did I get fewer rows than the match count?**

Two reasons, both deliberate. Kickstarter's sub-category ranking returns projects from neighbouring and unrelated categories, about one row in six, and those are dropped before they are charged. And any single search stops at 9,600 projects however many matches it reports. Both show up as free diagnostic rows so you can see which happened.

**What happens if I mistype a category or a state?**

The run stops before searching and writes a free row telling you what it accepts. It will not fall back to an unfiltered search, which is what Kickstarter itself would do and what would cost you money.

**How do I compare projects in different currencies?**

Use `goalUsd` and `pledgedUsd`. They are converted with the rate Kickstarter applies, and the goal and pledged filters work on those figures too, so the bands mean the same thing for every project.

**Will the run fail if something goes wrong?**

No. A blocked, empty or broken search produces an uncharged diagnostic row explaining what happened, and the run still finishes as succeeded. A failed run would still bill you the start fee, which would mean paying to be told something went wrong.

**Can I run this on a schedule?**

Yes. Nothing is held between runs, so the same input is safe to repeat. Diff on `projectId` to see only what is new since last time.

**How do I get exactly the rows I paid for?**

Filter the dataset on `"charged": true`. Sample and diagnostic rows are always `false`, and the number of charged rows always equals the number of billed events.

# Actor input Schema

## `state` (type: `string`):

Which projects to return. 'upcoming' means projects with a pre-launch page that have not started funding yet, so they have no goal, pledged total, backer count or deadline. Leave empty to search every state at once.

## `category` (type: `string`):

A Kickstarter category. Use a section on its own ('games', 'technology', 'design') or a full path ('games/tabletop games', 'food/restaurants'). A category id also works. Anything that is not a real Kickstarter category is refused before the run spends anything - Kickstarter itself would silently ignore it and hand back every project instead.

## `searchTerms` (type: `array`):

Words to search project pages for. Each term is searched in turn until the run reaches the row limit. Up to 20 per run. Leave empty to browse a category or state without a keyword.

## `location` (type: `string`):

A place, written the way Kickstarter writes it: 'Berlin, Germany', 'London, UK', 'California', 'United Kingdom'. The name is looked up first, and every row is then checked to come from there. A bare woe\_id number is also accepted, but rows cannot be checked against one.

## `sortBy` (type: `string`):

How Kickstarter orders the results before paging. 'Magic' is its own default blend. Note that deep paging stops at 9,600 projects per search whichever order you pick, so the sort decides which 9,600 you get.

## `fundingStatus` (type: `string`):

Filter by how far a project got towards its goal.

## `goalRange` (type: `string`):

Filter by the funding goal, converted to US dollars. A project asking 100,000 JPY counts as about $626, not as $100,000.

## `pledgedRange` (type: `string`):

Filter by how much a project has actually raised, converted to US dollars.

## `staffPicksOnly` (type: `boolean`):

Return only projects Kickstarter's own team has picked out.

## `searchUrls` (type: `array`):

Build the search you want on kickstarter.com/discover, then paste the address bar here. Every setting in the URL is checked against the same list the form uses, and anything Kickstarter would have silently dropped is reported back as a free note rather than sent.

## `maxItems` (type: `integer`):

Total projects to return across everything you asked for. The budget is shared evenly between your search terms and pasted URLs. Keep it low while you are testing - you pay per row.

## `proxyUrls` (type: `array`):

Leave this empty for a normal run. Fill it in only if you want the traffic to leave through proxy servers you already pay for, one URL per line, in the form http://user:pass@host:port.

## Actor input object example

```json
{
  "state": "live",
  "category": "games/tabletop games",
  "searchTerms": [],
  "sortBy": "magic",
  "searchUrls": [],
  "maxItems": 100
}
```

# Actor output Schema

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

Every row in the default dataset: searchLabel, projectId, name, blurb, creatorName, categoryName, categorySlug, categoryId, state, goal, pledged, currency, goalUsd, pledgedUsd, percentFunded, backersCount, deadline, launchedAt, createdAt, stateChangedAt, projectUrl, country, locationName, staffPick, isStaffSpotlight, hasVideo, imageUrl, creatorUrl, resultPage, totalMatches. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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 = {
    "state": "live",
    "category": "games/tabletop games",
    "searchTerms": [],
    "location": "",
    "sortBy": "magic",
    "searchUrls": [],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/kickstarter-projects-scraper").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 = {
    "state": "live",
    "category": "games/tabletop games",
    "searchTerms": [],
    "location": "",
    "sortBy": "magic",
    "searchUrls": [],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/kickstarter-projects-scraper").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 '{
  "state": "live",
  "category": "games/tabletop games",
  "searchTerms": [],
  "location": "",
  "sortBy": "magic",
  "searchUrls": [],
  "maxItems": 100
}' |
apify call dami_studio/kickstarter-projects-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/kickstarter-projects-scraper"
        }
    }
}
```

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/ho81IYFy7wluRPefO/builds/mNrhesTf7WpPhCIcm/openapi.json
