# Guru Scraper \[$0.90/1K💰] | Jobs | Budget | Client Spend (`ahmed_jasarevic/guru-scraper`) Actor

Scrape Guru.com freelance jobs by keyword and export title, budget, skills, client history, quote count, posting age and a direct link as clean JSON. Uses Guru's own JSON API, no browser. Built for freelance lead-gen and market research.

- **URL**: https://apify.com/ahmed_jasarevic/guru-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.88 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Guru Scraper — Freelance Jobs & Budgets API

Scrape freelance jobs from [Guru.com](https://www.guru.com/d/jobs/) into a clean dataset. Give it a keyword (or any Guru job-listing URL) and it returns one record per job with the **budget, skills, posting age, quote count, client history and a direct link**. It runs on plain HTTP — **no browser, no headless Chrome** — against Guru's own JSON search API first and the server-rendered listing pages as a fallback, which keeps runs fast and cheap at **$0.90 / 1,000 results**.

### Main use cases

- **Freelance lead generation** — collect fresh Guru jobs that match your skills and pitch them before the quote count climbs.
- **Freelance job monitoring** — schedule recurring searches and get new postings with budgets and client history as they appear.
- **Market and rate research** — track budgets, hourly rates and durations across categories to price your own services.
- **Freelance jobs database / marketplace intelligence** — build a spreadsheet or warehouse of Guru postings to analyse demand by skill, category and client country.
- **Client prospecting** — filter by `employerTotalSpent` and `employerCountry` to find clients who already spend on the platform.

### How it works

The actor searches Guru for each keyword you provide (or opens any Guru listing URL) and writes one record per job. It tries Guru's internal JSON search API first — one request carries **20 jobs**, with the full, un-truncated title, description, budget, skills and client history — and falls back to the server-rendered listing pages if the API is unavailable. It is a **CheerioCrawler** (HTTP-only, 256 MB, concurrency 3); nothing in the pipeline renders a page.

Every record carries `source: "api"` or `source: "html"` so a dataset always says which surface produced it.

### Extract Freelance Jobs From Guru.com For Lead Generation

| Field | Example |
| --- | --- |
| `title` | `FullStack Developer for Platform Rebuild` |
| `url` | `https://www.guru.com/jobs/fullstack-developer-for-platform-rebuild/2119273` |
| `jobId` | `2119273` |
| `budgetType` | `Hourly` / `Fixed Price` |
| `budgetAmount` | `$25 - $40`, `$1k-$2.5k`, `No Rate specified` |
| `budgetText` | `Hourly \| $25 - $40 \| 30+ hrs/wk \| 3-6 months` |
| `hoursPerWeek` | `30+ hrs/wk` |
| `duration` | `3-6 months` |
| `postedDate` / `postedDateIso` | `Jul 06, 2026` / `2026-07-06` |
| `deadline` | `Aug 05, 2026` (the "Send before" date) |
| `quotesCount` | `117` |
| `skills` | `["JavaScript", "Node.js", "ReactJS", …]` |
| `category` / `subcategory` | `Programming & Development` / `Web Development & Design` |
| `employerName` | `Eddie M` |
| `employerCountry` | `United States` |
| `employerTotalSpent` | `1400` (lifetime spend on Guru, `null` when not shown) |
| `description` | The full job post text |
| `titleFromDetail` | `true` when the title is the one Guru stores, not the listing's ~40-char truncation |
| `source` | `api` (Guru's JSON search API) or `html` (listing page) |
| `keyword` / `sourceUrl` | Which search produced this job |

### Build A Freelance Jobs Database Without The Guru API

Guru has no public API, so the actor uses the same internal surface Guru's own web app calls.

1. **Bootstrap.** One request to `https://www.guru.com/api/jobs/1/detailsstatisticssummary` answers `302` with `Set-Cookie: _accessToken=…` — an **anonymous** JWT Guru issues to every visitor. No login, no OAuth handshake, no page to render.
2. **Search.** Each keyword becomes `GET https://www.guru.com/api/search/job/?query=<base64-of-the-listing-path>` with `Authorization: Bearer <token>`: **20 jobs per page**, the stored title (no listing truncation), full descriptions, budget, skills and client history in one payload — a single request replaces a listing page *plus* one detail page per job. The cookie alone is not enough (401 without the bearer header), and `query` must be the base64 of the listing *path*.
3. **Fallback.** If the API answers `401`/`403` or anything unexpected (Imperva has flagged Apify's datacenter ranges), the run queues the original server-rendered listing pages and continues there — that path needs the residential proxy. Server-rendered pages carry the full data in plain HTML (`pageData.TotalResults` plus the `ul#ctl00_guB_ulpaginate` pager drive pagination; each job is a `<div class="record jobRecord" data-gid="…">`), so a CheerioCrawler parses it directly.

**Anti-bot (Imperva / Incapsula).** Three request properties decide whether `guru.com` answers with content or a challenge, and the actor pins all three — each was measured side-by-side through the same proxy IP, URL and headers, changing only one property at a time:

| Property | Wrong value → | Correct value (shipped) |
| --- | --- | --- |
| Protocol | HTTP/2 → 212-byte challenge | **HTTP/1.1** (`http2: false`) |
| TLS ClientHello | got-scraping's Chrome-cipher `tlsHook` → challenge | **stock OpenSSL** (`https.minVersion: 'TLSv1.2'`) |
| User-Agent | Chrome UA on Node TLS → HTTP 403 challenge | **`curl/8.5.0`** |

Imperva grades **UA ↔ TLS consistency**: a Chrome UA on a Node/OpenSSL ClientHello gets a `403`, while a plain `curl/8.5.0` UA on the identical request returns the full page. No paid anti-bot service and no browser are required.

**Other reverse-engineering notes.**

- **Keyword URL spelling.** Guru maps a free-text keyword onto a `/skill/{slug}/` path segment using the transform it ships in its own bundle (spaces → `-`, literal hyphens → `--`, then URL-encode), e.g. `web design` → `https://www.guru.com/d/jobs/skill/web-design/`. The legacy `/d/jobs/q/{slug}/` form answers `404`.
- **Block-page detection.** Real Guru pages *also* embed an Imperva monitoring script, so matching `_Incapsula_Resource` flags every valid page. The genuine block page is an ~800-byte document containing `Incapsula incident ID`, so the detector requires that banner (or a tiny body).
- **IP reputation matters for the HTML path.** The same listing request that fails from Apify's datacenter ranges passes from a residential proxy IP; the API is tried first and only falls back to HTML.
- **Failure handling.** Challenge pages and `>= 400` responses are detected, logged with a snippet of what Guru actually served, and the session is retired so the retry runs on a fresh session/proxy — instead of being silently written to the dataset.

### Input Parameters

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `keywords` | array of strings | No (prefilled) | `["web design", "python"]` | Search terms. Each becomes `https://www.guru.com/d/jobs/skill/{keyword}/`. |
| `startUrls` | array (requestListSources) | No | `https://www.guru.com/d/jobs/` | Direct listing URLs (category, skill, budget or saved-search pages). **When set, these override `keywords`.** |
| `maxResults` | integer | No | `60` | Maximum jobs to collect in total (max 2000). |
| `maxPagesPerKeyword` | integer | No | `5` | Pagination depth per search. 20 jobs/API page, 10 jobs/HTML page. |
| `fetchFullTitles` | boolean | No | `true` | In HTML fallback mode opens each job's page for the full title/description (+1 request per job). The JSON API always returns full titles. |
| `maxRequestsPerCrawl` | integer | No | `120` | Hard cap on HTTP requests; auto-raised if smaller than `maxResults` needs. |
| `proxy` | object (proxy) | No | `useApifyProxy: false` | Standard Apify proxy picker — use the **residential** group for the HTML path. |

Free accounts get a **10-job preview** per run; paid accounts run up to `maxResults`.

### Output Fields

One dataset item per job (24 fields). The default `overview` view shows Job title, Budget type, Budget, Quotes, Posted, Client and Job URL.

| Field | Type | Description |
|---|---|---|
| `jobId` | string | Guru project id (`data-gid`; also the last path segment of the job URL). |
| `title` | string | Job title, complete as Guru provides it. |
| `url` | string | Direct link to the job. |
| `budgetType` | string / null | `Hourly` or `Fixed Price`. |
| `budgetAmount` | string / null | Rate or fixed price as displayed, e.g. `$25 - $40`. |
| `budgetText` | string / null | Complete budget line as shown in the listing. |
| `hoursPerWeek` | string / null | Weekly commitment for hourly jobs. |
| `duration` | string / null | Expected project duration. |
| `postedDate` | string / null | Posted date as Guru displays it. |
| `postedDateIso` | string / null | Posted date normalized to `YYYY-MM-DD`. |
| `deadline` | string / null | The "Send before" date. |
| `quotesCount` | number / null | Number of quotes the job has received. |
| `description` | string / null | Full job description text. |
| `skills` | array of strings | Skills attached to the job. |
| `category` | string / null | Top-level Guru category. |
| `subcategory` | string / null | Guru subcategory. |
| `employerName` | string / null | Client who posted the job. |
| `employerCountry` | string / null | Client country. |
| `employerTotalSpent` | number / null | Lifetime spend on Guru (null when not displayed). |
| `titleFromDetail` | boolean | True when the title is Guru's stored title, not the ~40-char truncation. |
| `source` | string / null | `api` or `html`. |
| `keyword` | string / null | Search keyword this job was found under (null for explicit URLs). |
| `sourceUrl` | string | Listing/search URL this job was parsed from. |
| `scrapedAt` | string | ISO timestamp of when the job was parsed. |

### Example Input And Output

**Input**

```json
{
  "keywords": ["web design", "python"],
  "maxResults": 60,
  "maxPagesPerKeyword": 5,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

**Output (one dataset row)**

```json
{
  "jobId": "2119273",
  "title": "FullStack Developer for Platform Rebuild",
  "url": "https://www.guru.com/jobs/fullstack-developer-for-platform-rebuild/2119273",
  "budgetType": "Hourly",
  "budgetAmount": "$25 - $40",
  "budgetText": "Hourly | $25 - $40 | 30+ hrs/wk | 3-6 months",
  "hoursPerWeek": "30+ hrs/wk",
  "duration": "3-6 months",
  "postedDate": "Jul 06, 2026",
  "postedDateIso": "2026-07-06",
  "deadline": "Aug 05, 2026",
  "quotesCount": 117,
  "description": "We are rebuilding our SaaS platform and need a full-stack developer...",
  "skills": ["JavaScript", "Node.js", "ReactJS"],
  "category": "Programming & Development",
  "subcategory": "Web Development & Design",
  "employerName": "Eddie M",
  "employerCountry": "United States",
  "employerTotalSpent": 1400,
  "titleFromDetail": true,
  "source": "api",
  "keyword": "web design",
  "sourceUrl": "https://www.guru.com/d/jobs/skill/web-design/",
  "scrapedAt": "2026-07-06T09:12:03.000Z"
}
```

### Integrations & Automation

Every Apify actor is reachable through the **Apify API**, and this one plugs into **webhooks**, **Zapier** and **Make** so new jobs can be routed to Slack, a Google Sheet, Notion, Airtable or a CRM. For lead generation, run it on a **schedule** — a daily search for your skills is enough to catch fresh postings early, and recurring runs also improve the actor's Store recommendation signals. You can also run it from the Apify Console or via the API with `POST https://api.apify.com/v2/acts/ahmed_jasarevic~guru-scraper/runs`.

### Related Actors

- [upwork-job-scraper](https://apify.com/neatrat/upwork-job-scraper) — the largest Upwork jobs scraper; pairs Guru and Upwork coverage for freelance lead-gen.
- [upwork-scraper](https://apify.com/blackfalcondata/upwork-scraper) — broader Upwork marketplace data (jobs and profiles).
- [careerjet-scraper](https://apify.com/ahmed_jasarevic/careerjet-scraper) — job listings from a major global job aggregator.
- [myworkdayjobs-scraper](https://apify.com/ahmed_jasarevic/myworkdayjobs-scraper) — corporate job postings from Workday career sites.
- [superprof-scraper](https://apify.com/ahmed_jasarevic/superprof-scraper) — tutor/freelance marketplace listings to widen the freelance dataset.

### FAQ

#### Is there an official Guru.com API?

Guru does not offer a public API. This actor uses Guru's own internal JSON search API with the **anonymous** access token Guru issues to every visitor, so you get structured jobs without an API key or an account.

#### What are the alternatives to this actor / this data source?

For freelance-market data you can combine this with Upwork and Fiverr scrapers. Guru is a distinct, long-running freelance marketplace, so its postings, budgets and client spend complement the other platforms rather than duplicate them.

#### How do I get new freelance web-design or Python jobs?

Set `keywords` to the skills you care about (e.g. `["web design", "python"]`), choose `maxResults`, and schedule the actor daily. Each run returns the latest matching jobs with budget, skills, quote count and a direct link.

#### What does `employerTotalSpent` mean?

It is the amount the client has spent on Guru over their lifetime, as shown on the listing. It is `null` when Guru does not display it — it is a useful signal for filtering out low-value clients.

#### Do I need a proxy?

The JSON search API has answered from datacenter IPs, so many runs need no proxy. The HTML fallback path sits behind Imperva and requires the **residential** proxy group. The actor tries the API first and only needs the proxy when it falls back.

#### How is this cheaper than other Guru scrapers?

It returns **20 jobs per request** with the full title and description, so a single API call replaces a listing page plus one detail page per job. At **$0.90 / 1,000 results** it is the lowest per-result Guru price observed in the Store.

### SEO Keywords

guru, guru.com, guru scraper, guru jobs, guru freelance, guru api, freelance jobs api, freelance lead generation, freelance job monitoring, upwork scraper, fiverr scraper, freelance marketplace data, job budget tracking, client spend data, python jobs, web design jobs

### For AI Agents & LLM Apps

Purpose: return one structured freelance-job row per Guru.com job for a given keyword or listing URL.

Minimal working input:

```json
{ "keywords": ["web design"], "maxResults": 60 }
```

Variant (URL-driven, overrides keywords):

```json
{ "startUrls": [{ "url": "https://www.guru.com/d/jobs/c/programming-development/" }], "maxResults": 60 }
```

Output fields: `jobId, title, url, budgetType, budgetAmount, budgetText, hoursPerWeek, duration, postedDate, postedDateIso, deadline, quotesCount, description, skills, category, subcategory, employerName, employerCountry, employerTotalSpent, titleFromDetail, source, keyword, sourceUrl, scrapedAt`.

Behaviors an agent should know:

- **`startUrls` overrides `keywords`** when both are set.
- If `maxResults` is left unset it defaults to **60**; free-plan runs are capped at **10 jobs**.
- `source` is `api` or `html`; the JSON API path is tried first and needs no proxy, the HTML fallback needs a residential proxy.
- `fetchFullTitles: true` (default) only adds requests in HTML fallback mode; the JSON API always returns full titles.
- `keyword` is `null` when the job came from an explicit `startUrls` entry.
- `employerTotalSpent` may be `null` when Guru does not display it.

Billing: pay-per-event — **$0.0009 per result** ($0.90 / 1,000 results) plus a $0.00005 actor-start event.

### Legal & Compliance Disclaimer

This actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by Guru**. It accesses only publicly available Guru.com pages and the anonymous access token Guru issues to every visitor; it does not log in, bypass authentication, or solve CAPTCHAs on anyone's behalf. Users are responsible for ensuring their use complies with Guru's Terms of Service and robots.txt, and with applicable data-protection law. Output may include client names and countries; where personal contact data is present it must not be used for unsolicited commercial outreach in violation of applicable law (e.g. CAN-SPAM, GDPR).

# Actor input Schema

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

Keywords to search for. Each one becomes a Guru job-search URL: https://www.guru.com/d/jobs/skill/{keyword}/

## `startUrls` (type: `array`):

Direct Guru listing URLs to start from (e.g. https://www.guru.com/d/jobs/c/programming-development/ or a /d/jobs/skill/{keyword}/ URL). When set, these override the keywords.

## `maxResults` (type: `integer`):

Maximum number of job listings to collect in total.

## `maxPagesPerKeyword` (type: `integer`):

Safety cap on pagination depth for each search. Guru returns 20 jobs per page through its JSON search API and 10 per page on the HTML listing fallback.

## `fetchFullTitles` (type: `boolean`):

Guru truncates job titles to ~40 characters in the HTML search results. When enabled and the JSON search API is unreachable, the Actor also opens each job's detail page to get the full title and full description (costs one extra request per job). The JSON search API returns the title Guru stores, without the listing's truncation, so this only affects the HTML fallback mode. Disable for a faster, cheaper run.

## `maxRequestsPerCrawl` (type: `integer`):

Hard cap on total HTTP requests made by this crawler. Auto-raised if it is smaller than maxResults (+ pages) - each job with "Fetch full titles" enabled costs one listing request plus one detail request.

## `proxy` (type: `object`):

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "keywords": [
    "web design",
    "python"
  ],
  "startUrls": [
    {
      "url": "https://www.guru.com/d/jobs/"
    }
  ],
  "maxResults": 60,
  "maxPagesPerKeyword": 5,
  "fetchFullTitles": true,
  "maxRequestsPerCrawl": 120,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "keywords": [
        "web design",
        "python"
    ],
    "startUrls": [
        {
            "url": "https://www.guru.com/d/jobs/"
        }
    ],
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/guru-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 = {
    "keywords": [
        "web design",
        "python",
    ],
    "startUrls": [{ "url": "https://www.guru.com/d/jobs/" }],
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/guru-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 '{
  "keywords": [
    "web design",
    "python"
  ],
  "startUrls": [
    {
      "url": "https://www.guru.com/d/jobs/"
    }
  ],
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call ahmed_jasarevic/guru-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/guru-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/LIXWCZrRXu7fo7fTS/builds/7qjLLy8NAKJ0LsVoK/openapi.json
