# Odoo Apps Store Scraper — Modules and Prices (`atesen-software/odoo-apps-store-scraper`) Actor

Scrape apps.odoo.com: module name, technical name, vendor, price and currency, supported Odoo versions, license, rating, purchases or downloads, dependencies and short description. Filter by Odoo version, category, vendor or search term. No login, no proxy. Pay per result — see the Pricing tab.

- **URL**: https://apify.com/atesen-software/odoo-apps-store-scraper.md
- **Developed by:** [Atesen Software](https://apify.com/atesen-software) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 successful module results

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

Scrape the Odoo Apps Store: module names, technical names, vendors, prices, supported Odoo versions, licenses, ratings and downloads. No login needed.

### Odoo Apps Store Scraper (modules, prices, versions)

Point it at apps.odoo.com with search terms, category or vendor listings, or single module pages, and get one row per module: **name, technical name, URL, vendor, price and currency, supported Odoo versions, license, rating, purchases or downloads, dependencies, last release-note date and short description**. It reads the public store pages only, one page per second, and obeys the store's `robots.txt`. No API key, no browser, no proxy, no login.

- **Filter by Odoo version**: 17.0, 18.0, 19.0 and so on. Searches and listings are run once per version; a module URL is read for each version you list.
- **Three ways in**: search terms, listing URLs (a category or a vendor), and module URLs. Mix them in one run.
- **Fast list mode**: turn off "Read each module page" and get 20 modules per request with name, vendor, price, votes, purchases or downloads and summary.
- **Never crashes on a missing page**: a module that does not exist for a version, a blocked URL or an unsupported link gives one free `status` row that says why.
- **No personal data fields**: only the vendor name printed in the store is saved. Reviewer names, comments, support e-mail addresses, phone numbers and vendor websites are not read as fields; e-mail addresses are also removed from names and summaries. A summary is kept as the vendor wrote it.

### Use cases

- **Competitor research for Odoo partners and module developers**: who sells a Shopify, WhatsApp or POS connector, for which versions, under which license.
- **Pricing research**: compare list prices across vendors for the same job, free against paid, and how many sales a module has had in total and last month.
- **Version coverage checks**: which modules already support Odoo 19.0 or 20.0, and which still stop at 17.0.
- **Dependency mapping**: which standard Odoo apps and bundled community modules a module needs.

### Input

| Field | Description |
|---|---|
| `searchTerms` | Words such as `shopify` or `pos restaurant`. A module matches when all words appear in its name, technical name, vendor or summary. |
| `startUrls` | Optional. Listing URLs (`.../apps/modules/category/Accounting/browse`, `.../apps/modules/browse?author=VentorTech`) and module URLs (`.../apps/modules/18.0/integration_shopify`). A listing URL that already has `?series=` keeps that version. |
| `odooVersions` | Optional. For example `17.0`, `18.0`. Empty = all versions for searches, the URL's own version for module URLs. |
| `maxItems` | Total module rows for the whole run. Default 50. `0` = no limit. |
| `maxListingPages` | Listing pages read per term, listing and version. Default 10; a page holds 20 modules. |
| `includeDetails` | Default on. Reads each module page for versions, license, rating, dependencies and more. Off = fast list mode. |
| `includeSourceInfo` | Default on. One free `source` row per term, listing or module URL. |

```json
{
  "searchTerms": ["shopify"],
  "odooVersions": ["18.0"],
  "maxItems": 5,
  "maxListingPages": 10
}
```

**How search works.** Odoo's own search URL (`?search=`) is closed to robots in `robots.txt`, so this Actor never calls it. Search terms are matched against the store's normal listing pages, which come in the store's default ranking (popular modules first). The `source` row tells you how many pages and entries were scanned and whether the whole listing was read (`complete`). For a deeper search, raise `maxListingPages` or add a category URL.

### Output

One row per module and Odoo version. `odooVersion` is the store URL/listing series (for example `18.0`); the detail-only `softwareVersion` is the module release (for example `18.0.1.0.0`), or `null` when missing. Long text is cut at 300 characters with "…".

```json
{
  "type": "module",
  "name": "Odoo Shopify Connector PRO",
  "technicalName": "integration_shopify",
  "url": "https://apps.odoo.com/apps/modules/18.0/integration_shopify",
  "odooVersion": "18.0",
  "vendor": "VentorTech",
  "price": 499,
  "currency": "EUR",
  "priceText": "499.00 €",
  "isFree": false,
  "category": "Sales",
  "priceModuleOnly": 449,
  "license": "OPL-1",
  "supportedVersions": ["13.0", "14.0", "15.0", "16.0", "17.0", "18.0", "19.0", "20.0"],
  "ratingValue": 5,
  "ratingVotes": 22,
  "reviewCount": 55,
  "purchases": 259,
  "purchasesLastMonth": 8,
  "downloads": null,
  "downloadsLastMonth": null,
  "lastUpdate": "2026-09-29",
  "shortDescription": "Export products and your current stock from Odoo to Shopify, and seamlessly import orders from your Shopify store into Odoo. Update order status and provide tracking numbers to your customers automatically and instantly! This Shopify connector simplifies multi-channel e-commerce by automating data…",
  "depends": ["mrp", "account", "mail", "stock"],
  "dependsCommunity": ["integration", "queue_job"],
  "linesOfCode": 4329,
  "odooOnline": false,
  "odooSh": true,
  "onPremise": true,
  "input": "shopify",
  "scrapedAt": "2026-10-24T00:41:50.131Z"
}
```

Field notes:

- `price` is the price the store shows. When a module bundles community modules, that price includes them; `priceModuleOnly` is then the module's own price. Free modules have `price: 0`, `currency: null`, `isFree: true`. If the price block cannot be read (the store changed its page), `price`, `currency`, `priceText` and `isFree` are all `null`: an unknown price is never reported as 0 or free.
- `currency` is an ISO code read from the symbol the store prints for the visitor's region (`EUR` in the runs shown here). The same module can show another currency from another region.
- `purchases` counts sales of a paid module and `downloads` counts downloads of a free one. The "last month" figures come from the listing, so they are `null` for a module read from its URL alone. The same holds for `shortDescription`.
- `lastUpdate` is the newest full date (YYYY-MM-DD) found in the release notes on the module's documentation tab. The store prints no update date of its own, so it is `null` for modules whose vendor writes none.
- `ratingValue`, `ratingVotes` and `reviewCount` are `null` for modules without ratings. `ratingValue` and `reviewCount` come from the module page, so they are missing in fast list mode. `reviewCount` is the figure from the page's metadata and can be higher than the votes of this version.
- `depends` lists the standard Odoo apps a module needs (technical names); `dependsCommunity` lists bundled community modules. Both are `null` when the page shows none.
- In fast list mode a row holds only the fields the listing shows: name, technical name, URL, version, vendor, price, votes, purchases or downloads (with last month), summary and `input`.

Free helper rows share the same dataset:

- `type: "source"`: `input`, `kind` (`search`, `listing` or `module`), `odooVersion`, `listingPagesRead`, `entriesScanned`, `entriesMatched`, `entriesInListing` (size of the whole listing), `itemsSaved`, `complete`. A module saved for an earlier input in the same run is not saved twice, so `itemsSaved` can be lower than `entriesMatched`. A module URL that cannot be read gives a status row and no source row.
- `type: "status"`: `status`, `input`, `url`, `httpStatus`, `message`.

Status values you may see: `module_not_found` (HTTP 404 for that version), `blocked_by_robots`, `not_found`, `access_denied`, `fetch_failed`, `detail_failed`, `listing_parse_failed` (the store counted apps but no module card could be read, so the listing is not marked `complete`), `no_results`, `unsupported_url`, `invalid_url`, `invalid_input`.

When "Read each module page" is on and one module page cannot be read, the module is still saved from its listing entry (without the page fields) and a free `detail_failed`, `fetch_failed` or similar status row names the module.

```json
{
  "type": "status",
  "status": "blocked_by_robots",
  "input": "https://apps.odoo.com/apps/modules/browse?search=shopify",
  "message": "The store's robots.txt does not allow reading https://apps.odoo.com/apps/modules/browse?search=shopify&series=17.0. Skipped to stay polite. Odoo disallows its own search and sorting URLs, so use \"Search terms\" instead of a search URL. Not charged.",
  "url": "https://apps.odoo.com/apps/modules/browse?search=shopify&series=17.0",
  "httpStatus": null,
  "scrapedAt": "2026-10-24T00:43:04.027Z"
}
```

### Limits

- Only modules on apps.odoo.com. Themes, the Odoo Enterprise apps pages and anything behind a login are not read.
- Search is a scan of the listing, not the store's search engine (see above). It finds the active modules first and may miss rarely sold ones unless you read more pages.
- Prices are list prices as shown to a visitor. Discounts, taxes and the maintenance price per 100 lines of code are not collected.
- One request per second, with retries and back-off. A full run over thousands of modules is slow by design.
- The store changes its pages now and then. If a page no longer looks like a module page you get a `detail_failed` status row instead of a wrong row.

### FAQ

**Is it allowed to read the Odoo Apps Store?** The Actor reads public pages only and checks `https://apps.odoo.com/robots.txt` first. On 24 October 2026 that file closed the search, sort and `?page=` URLs but left category, vendor and version listings and module pages open, which are the only URLs this Actor uses. Odoo's published legal pages (subscription agreement, cloud acceptable-use policy, sales conditions) contain no rule about reading public store pages, but the cloud policy bans crawling that hurts availability, which is why the Actor stays at one request per second. You remain responsible for how you reuse the data.

**Why does a search find fewer modules than the store's search box?** The store's search URL is disallowed for robots, so the Actor scans listing pages and filters them locally. Raise `maxListingPages` for more depth, or give a category URL to narrow the area first. The `complete` flag in the `source` row shows whether the whole listing was read.

**Why is `price` sometimes higher than `priceModuleOnly`?** Some modules bundle the community modules they need, and the store shows one price for the bundle. `priceModuleOnly` is the part for the module itself.

**Does it collect personal data?** No contact details, reviews or reviewer names are read. The `vendor` is the publisher name printed in the store, which for a few modules is a developer's own name; check the laws that apply to you before you reuse it.

**Which Odoo versions are covered?** Every version the store lists, today 5.0 to 20.0. `supportedVersions` shows which versions a module exists for.

### Pricing

Pay per result — see the Pricing tab.

Each saved module row is one result. Source rows and status rows are free. The run stops when it reaches the maximum cost per run that you set.

### Support and disclaimers

Questions and feature requests belong on the **Issues** tab of this Actor's page on Apify. This Actor is an independent tool and is not affiliated with Odoo S.A. or any module vendor. It reads only what the Odoo Apps Store publishes to visitors; how you may use that data depends on the store's terms and the applicable law.

# Actor input Schema

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

Words to look for, for example "shopify" or "pos restaurant". A module matches when all words appear in its name, technical name, vendor or summary. Odoo's own search page is closed to robots (robots.txt), so the terms are matched against the store's listing pages in the store's default ranking (popular modules first), up to "Max listing pages" per term and version.

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

Optional. apps.odoo.com listing URLs such as https://apps.odoo.com/apps/modules/category/Accounting/browse or https://apps.odoo.com/apps/modules/browse?author=VentorTech, and module URLs such as https://apps.odoo.com/apps/modules/18.0/integration_shopify. A listing URL that already contains ?series= keeps that version.

## `odooVersions` (type: `array`):

Optional. Odoo versions such as 17.0 or 18.0. Searches and listings are limited to each version in turn. A module URL is read for each version listed here (a version the module does not have gives a free status row). Empty = the versions as they appear in the URLs, or all versions for searches.

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

Stop after this many module rows in total. 0 = no limit (the pages limit below still applies).

## `maxListingPages` (type: `integer`):

Listing pages read per search term, listing URL and version. Each page holds 20 modules, so 10 pages scan 200 modules. The source row shows whether the whole listing was read.

## `includeDetails` (type: `boolean`):

Adds supported versions, license, rating value, dependencies, release-note date, lines of code and platform availability (one extra request per module). Turn off for a fast list of 20 modules per request with name, vendor, price, votes, purchases or downloads and summary.

## `includeSourceInfo` (type: `boolean`):

One free row per search term, listing or module URL: pages read, entries scanned, modules saved, and whether the whole listing was read.

## Actor input object example

```json
{
  "searchTerms": [
    "shopify",
    "whatsapp"
  ],
  "odooVersions": [
    "18.0"
  ],
  "maxItems": 50,
  "maxListingPages": 10,
  "includeDetails": true,
  "includeSourceInfo": true
}
```

# Actor output Schema

## `modules` (type: `string`):

No description

## `allFields` (type: `string`):

No description

## `summary` (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 = {
    "searchTerms": [
        "shopify",
        "whatsapp"
    ],
    "odooVersions": [
        "18.0"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("atesen-software/odoo-apps-store-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 = {
    "searchTerms": [
        "shopify",
        "whatsapp",
    ],
    "odooVersions": ["18.0"],
}

# Run the Actor and wait for it to finish
run = client.actor("atesen-software/odoo-apps-store-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 '{
  "searchTerms": [
    "shopify",
    "whatsapp"
  ],
  "odooVersions": [
    "18.0"
  ]
}' |
apify call atesen-software/odoo-apps-store-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,atesen-software/odoo-apps-store-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/HifmJdvxK1bXyZb5A/builds/W8fsZ9IEw6Jcv4QHQ/openapi.json
