# PR TIMES Sales & Investment Signal Classifier (`woolen_snake/prtimes-sales-signal-classifier`) Actor

An Apify Actor that searches PR TIMES press releases by keyword or industry, returning company name, industry, and official release type, plus simple tags for sales-timing signals like funding, partnerships, M\&A, new offices, major contracts, IPOs, and executive changes. Pay-per-event pricing.

- **URL**: https://apify.com/woolen\_snake/prtimes-sales-signal-classifier.md
- **Developed by:** [coco zizi](https://apify.com/woolen_snake) (community)
- **Categories:** Lead generation, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PR TIMES Sales & Investment Signal Classifier

Searches [PR TIMES](https://prtimes.jp) press releases by **keyword or industry category** and returns
each release tagged with the company name and PR TIMES' official classification (release type, business
category, keyword tags), plus a simple tag for **sales-approach / investment-timing signals** such as
**funding, business alliances, M\&A, new office openings, large orders, IPOs, and personnel changes**.
Pay-per-event pricing.

Rather than dumping a raw list of press releases, this Actor structures each individual release as a
"company move" and adds sales-lead / investment-signal value on top — the differentiation point of this
Actor (same stack as our BOOTH and SUUMO Actors: JavaScript ESM, Apify SDK, Pay-per-event pricing).

### ⚠️ Disclaimer (please read)

- This Actor automatically retrieves information that is publicly available on PR TIMES. **You are
  responsible for how you retrieve and use this data.**
- PR TIMES' [Terms of Use](https://prtimes.jp/main/html/kiyaku) (general terms and press terms) **contain
  a clause prohibiting the use of corporate content for paid purposes.** If you publish or operate this
  Actor commercially / with Pay-per-event pricing, please make that decision with the understanding that
  it may conflict with that clause. The developer makes no legal warranty on this point. **How you scope
  the release (private / restricted / public on the Store) is a separate decision you should make with
  this contractual risk in mind.**
- **The site owner's Terms of Use, robots.txt, or access blocking could change without notice, which
  could stop this Actor from working or change its output unexpectedly.**
- "Sales timing signal" tags are a **simple estimate based on keyword matching** against PR TIMES'
  official keyword/classification tags and the headline/description — they are not guaranteed to be
  accurate or exhaustive. Please verify the actual release body before acting on any sales or investment
  decision.

#### Facts confirmed during pre-build research (as of August 6, 2026)

- **robots.txt**: `User-agent: *` / `Allow: /` — crawling is allowed across the board except for the
  "like count" API (`https://prtimes.jp/robots.txt`).
- **CDN / bot protection**: The site runs behind Fastly (`Via: 1.1 varnish`, `X-Served-By: cache-nrt-*`);
  no Cloudflare-style JS challenge or CAPTCHA was observed. Main content is server-side rendered by
  Next.js and can be fetched with a regular HTTP client (no headless browser needed). Note this was only
  verified with single, low-frequency requests — rate-limiting behavior under sustained high-volume
  access has not been tested.

### Privacy / PII design

Some PR TIMES press release bodies were confirmed to contain a section such as "Contact for this
release" with an **individual staff member's name and email address** written as free text (not a
labeled, structured field — it's embedded in the prose). By contrast, the "Company Overview" block at
the end of an article (representative name, address, phone number, etc.) is explicitly labeled by PR
TIMES itself as the company's public information, which is a different kind of data from the "direct
personal contact info" our SUUMO Actor was designed to guard against.

With that in mind, this Actor applies two layers of defense:

1. **Limiting how much body text is captured** (`extractBodyParagraphs` in `src/prtimesClient.js`):
   paragraph collection stops as soon as a heading containing "contact/inquiry"-type wording is reached,
   and only the first 400 characters are kept as `bodyExcerpt`.
2. **A safety net right before output** (`src/privacyFilter.js`): free-text fields such as
   `bodyExcerpt`, `headline`, and `description` are scanned for email-address / phone-number-like regex
   patterns and person/contact labels (e.g. "Contact:", "Person in charge:"); any match nulls out that
   field. Detections are recorded in the output's `personalInfoRedactions` field. **`companyOverview`
   (the Company Overview block) is intentionally excluded from this safety net**, since it is the
   company's own public information rather than an individual's contact details.

This was verified against an actual article that contained a staff member's name and email address (a
Nagoya Chamber of Commerce and Industry IPO seminar announcement) — the `bodyExcerpt` was confirmed to
not include that information. That said, future changes to PR TIMES' markup or heading wording could
introduce PII into an unexpected field, so **always review the output before redistributing it
externally.**

### What it does

- **byKeyword mode**: search press releases by keyword (e.g. "funding", "DX")
- **byCategory mode**: search press releases by industry category ID (top level, optionally narrowed to
  a sub-category)
- For each release, it attaches:
  - Company name, PR TIMES' official **release type** (one of 7 categories: Product/Service, Event,
    Campaign, Corporate/Management Info, Survey Report, People, Other), **business category** (industry,
    top-level + sub-level), and **keyword tags** (PR TIMES' own official tags on the release)
  - **Sales timing signals** (`salesTimingSignals`): tagged when a keyword match is found for funding /
    business alliance-collaboration / M\&A-acquisition-merger / new office-store opening / large order-
    contract signing / IPO / personnel change-management reshuffle (`src/classify.js`)
  - Company overview (address, representative name, capital, listing status, etc.) and a body excerpt
    (first 400 characters, contact section excluded)
- Turning on `onlySalesSignal` limits output and billing to releases where a signal was detected

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `byKeyword` | `byKeyword` (search by keyword) / `byCategory` (search by industry category) |
| `keyword` | string | `""` | Search keyword, used when `mode` is `byKeyword` |
| `businessCategoryId` | string | `""` | Top-level industry category ID, used when `mode` is `byCategory` (e.g. `"015"`) |
| `businessCategorySubId` | string | `""` | Sub-category ID, used when `mode` is `byCategory` (optional, e.g. `"050"`) |
| `onlySalesSignal` | boolean | `false` | When on, only outputs and charges for releases with a sales timing signal |
| `maxItems` | integer | `20` | Cap on the number of releases output/charged (max 200) |
| `maxDetailFetches` | integer | `60` | Safety cap on how many detail pages are actually fetched from the search results (max 500) |
| `requestDelayMs` | integer | `1500` | Delay between requests in ms. Cannot be set below 1000ms for safety |

For `businessCategoryId`/`businessCategorySubId`, open the target category page
(`https://prtimes.jp/main/html/searchbiscate/busi_cate_id/[top-level]/lv2/[sub-level]/`) and read the ID
off the URL.

### Example output

The following is actual output from a real run of this Actor (as of August 6, 2026, keyword "資金調達" /
"funding"). Company names, headlines, and body text are naturally in Japanese, since that's the language
of the source data.

```json
{
  "sourceUrl": "https://prtimes.jp/main/html/rd/p/000000471.000032589.html",
  "companyName": "フォースタートアップス株式会社",
  "headline": "【STARTUP DB】調査結果　スタートアップ資金調達金額ランキング（2026年7月）",
  "description": "【STARTUP DB】調査結果　スタートアップ資金調達金額ランキング（2026年7月）　フォースタートアップス株式会社のプレスリリース",
  "datePublished": "2026-08-06 08:00:02",
  "dateModified": "2026-08-06 09:44:57",
  "releaseType": { "id": "05", "label": "調査レポート" },
  "businessCategory": {
    "major": { "id": "015", "label": "ビジネス・コンサルティング" },
    "minor": { "id": "50", "label": "経営・コンサルティング" }
  },
  "businessCategoriesAll": ["経営・コンサルティング", "政治・官公庁・地方自治体"],
  "keywords": ["ランキング", "資金調達", "スタートアップ", "投資", "CO2", "生成AI", "自動運転", "成長産業", "起業家"],
  "salesTimingSignals": [
    { "type": "funding", "label": "資金調達", "matchedTerms": ["資金調達"] }
  ],
  "isSalesTimingSignal": true,
  "companyOverview": {
    "url": "https://forstartups.com/",
    "industry": "情報通信",
    "headquartersAddress": "東京都港区麻布台1-3-1 麻布台ヒルズ 森JPタワー 31F",
    "phone": "03-6893-0650",
    "representativeName": "志水雄一郎",
    "listingStatus": "東証グロース",
    "capitalYen": "2億3900万円",
    "establishedYearMonth": "2016年09月"
  },
  "bodyExcerpt": "【STARTUP DB】調査結果 スタートアップ資金調達金額ランキング（2026年7月） フォースタートアップス 成長産業支援事業を推進するフォースタートアップス株式会社（本社：東京都港区、代表取締役 CEO：志水 雄一郎・代表取締役 COO：恒田 有希子 以下、フォースタートアップス）は、継続的にスタートアップ動向に関する調査を行い...",
  "disclaimer": "PR TIMES上の公開情報を基に自動生成した参考データです。取得・利用は利用者ご自身の責任で行ってください。...",
  "scrapedAt": "2026-08-06T09:50:00.000Z",
  "personalInfoRedactions": []
}
```

### Classification & signal-detection logic

1. Company name, headline, publish date, and PR TIMES' official "release type" / "business category" are
   read from the JSON-LD (schema.org `NewsArticle` / `BreadcrumbList`) embedded in each article page
   (`src/prtimesClient.js`). This structured data is prioritized because it's less exposed to CSS class
   hash changes than markup-based scraping.
2. Keyword tags and the Company Overview block are extracted by matching `dt`/`dd` label text (the same
   label-based extraction approach used in our SUUMO Actor).
3. PR TIMES' own keyword tags, the headline, description, and business category names are scanned against
   a sales-timing-signal keyword dictionary (`src/classify.js`) to attach `salesTimingSignals`.
   - The dictionary covers 7 signal types: funding, business alliance/collaboration, M\&A/acquisition/
     merger, new office/store opening, large order/contract signing, IPO, and personnel/management
     change.
   - This is a keyword-match estimate only, and can both over- and under-detect.

### Pricing (Pay-per-event)

| Event | Price | Trigger |
|---|---|---|
| Actor Start (`apify-actor-start`) | $0.00005 | Once per run (Apify's standard synthetic event) |
| Release classified (`release-classified`) | $0.015 | Per release output, with company name and official classification |
| Sales timing signal detected (`sales-timing-signal-detected`) | $0.045 | Charged in addition to release-classified for any release where a sales timing signal was detected |

A release with a detected signal costs $0.06 in total; a release with no signal costs $0.015.

#### How the price was set (measured on Apify, August 6, 2026)

We ran this Actor twice with production-like input (byKeyword mode with "funding" / 資金調達, and
byCategory mode with one industry category, 10-15 items each) and checked Apify's measured real
infrastructure cost (`usageTotalUsd`, covering Compute Units, data transfer, and storage writes).

| Run | Items output | Signals detected | Duration | Real infra cost | Cost per item |
|---|---|---|---|---|---|
| byKeyword ("資金調達"/funding) | 10 | 10/10 | 22.2s | $0.0013416 | $0.0001342 |
| byCategory (1 industry) | 15 | 6/15 | 28.3s | $0.0017052 | $0.0001137 |
| Blended | 25 | 16/25 | — | $0.0030468 | $0.0001219 |

About 92% of the cost is ACTOR\_COMPUTE\_UNITS (storage writes etc. are negligible). **Importantly, signal
detection (`src/classify.js`) is just a regex scan over data already fetched — it makes no additional
HTTP requests, so there is effectively no infrastructure cost difference between a release with a signal
and one without.** That means the price gap is not cost-based; it's set based on delivered value ("this
tells you why to reach out to this company right now"), following the same approach as our SUUMO Actor
(which priced $0.08 against a measured infra cost of about $0.0004/item, roughly a 200x margin):

- `release-classified` is set to $0.015, based on the higher of the two measured per-item costs
  ($0.0001342/item), which is still **more than 100x** that figure (112-132x across the two runs).
- `sales-timing-signal-detected` adds essentially zero infrastructure cost, so a cost-based calculation
  doesn't apply here. Since a genuine sales opportunity signal is worth more than a plain classification,
  it's priced at an additional $0.045 (3x the base event, 4x total for a signal-tagged release). That
  said, `salesTimingSignals` is a simple keyword-dictionary estimate with no accuracy guarantee, so it is
  priced well below what a commercial lead-generation service would typically charge (often $1 to tens of
  dollars per lead).

### Load management

There's a default **1500ms** delay between requests (search results list and detail pages).
Configurable via `requestDelayMs`, but it cannot be set below 1000ms for safety.

### Known limitations / things to verify before production use

- The selectors and URL patterns in `src/prtimesClient.js` were verified against real PR TIMES response
  HTML (keyword search pages, business category pages, article detail pages) as of August 6, 2026.
  PR TIMES' markup may change over time; if this stops working, run `npm run test-connectivity` to inspect
  the actual response and adjust accordingly.
- **Keyword search (`byKeyword`) only covers the first page of results.** Pagination on PR TIMES'
  keyword landing page (`/topics/keywords/{keyword}`) is JS-driven (a "load more" button), and this v0.1
  does not crawl additional pages. If you need a larger volume, `byCategory` mode with
  `businessCategoryId` may surface a wider pool of candidates
  (`/main/html/searchbiscate/busi_cate_id/.../` does support normal link-based pagination, but this v0.1
  only covers its first page as well).
- `salesTimingSignals` is a simple keyword-dictionary estimate. Wording not covered by the dictionary
  (spelling variants, English terms, etc.) will not be detected. The dictionary can be tuned via
  `SIGNAL_DEFINITIONS` in `src/classify.js`.
- The official PR TIMES "release type" taxonomy (`release_type_id`) is assumed to be the 7 categories
  (01-07) actually confirmed from real articles. If PR TIMES adds or changes categories in the future,
  `releaseType.label` will still follow along (since it uses the label text from the page link as-is),
  but the sales-signal dictionary's mapping to any new category would need a fresh review.
- A "full sync" feature that crawls everything across multiple runs (equivalent to our BOOTH Actor's
  `startPage`/`SYNC_STATE`) is not implemented.

# Actor input Schema

## `mode` (type: `string`):

byKeyword: search press releases matching the word in keyword. byCategory: search the press release listing for the industry category given by businessCategoryId (and optionally businessCategorySubId).

## `keyword` (type: `string`):

The term used for PR TIMES keyword search (e.g. "funding", "DX", "資金調達"). Used when mode is byKeyword.

## `businessCategoryId` (type: `string`):

The top-level category ID (e.g. "015") shown in the URL of a PR TIMES business-category listing page (https://prtimes.jp/main/html/searchbiscate/busi\_cate\_id/\[this value]/).

## `businessCategorySubId` (type: `string`):

The sub-category ID (e.g. "050") when narrowing down further, shown in the URL https://prtimes.jp/main/html/searchbiscate/busi\_cate\_id/\[businessCategoryId]/lv2/\[this value]/. Leave blank to cover the whole top-level category.

## `onlySalesSignal` (type: `boolean`):

When enabled, only releases where at least one sales-timing signal (funding, business alliance, M\&A, new office, large order, IPO, personnel change) was detected are output and charged. Releases with no signal are skipped and not charged.

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

Maximum number of releases to classify and output. A charge event is generated for every item, so this directly drives run cost.

## `maxDetailFetches` (type: `integer`):

Upper limit on how many release detail pages are actually fetched from the search results list (when onlySalesSignal is on, this also counts releases skipped without a signal and not charged).

## `requestDelayMs` (type: `integer`):

Delay between requests to reduce load on the PR TIMES server. Cannot be set below 1000ms for safety.

## Actor input object example

```json
{
  "mode": "byKeyword",
  "keyword": "",
  "businessCategoryId": "",
  "businessCategorySubId": "",
  "onlySalesSignal": false,
  "maxItems": 20,
  "maxDetailFetches": 60,
  "requestDelayMs": 1500
}
```

# Actor output Schema

## `classifiedReleases` (type: `string`):

Press releases tagged with PR TIMES' official release type/business category and heuristic sales-timing signals (funding, business alliance, M\&A, etc.).

# 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("woolen_snake/prtimes-sales-signal-classifier").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("woolen_snake/prtimes-sales-signal-classifier").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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 woolen_snake/prtimes-sales-signal-classifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=woolen_snake/prtimes-sales-signal-classifier",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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