# VK Scraper (`mlg14/vk-scraper`) Actor

Collect public VK wall posts and direct posts from handles, URLs, and owner IDs. Export text, dates, authors, engagement, attachments, and repost context with date and count limits.

- **URL**: https://apify.com/mlg14/vk-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.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.

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

## VK Scraper

Scrape public VK wall posts from a handle, community, profile, wall URL, owner ID, or direct post link. Export VK post data to JSON, CSV, or Excel with text, dates, public author details, engagement counts, attachments, and repost context. This is a practical VK API alternative for public post collection without supplying a personal login.

A run saves one dataset row per unique post. It uses structured public responses, follows older wall pages when needed, and stops at the total or per-target limit you choose. Private material and content that requires a login are outside its scope.

### What data can you extract from VK?

The output keeps a stable top-level shape. Optional fields are omitted when the source does not provide them. A media-only post may have an empty text value; a post without attachments has an empty media array.

| Field | Description | Example |
| --- | --- | --- |
| target | Submitted value that first found the post | "-230816011" |
| targetType | How that value was recognized | "ownerId" |
| postId | Stable owner and post identifier for joins and deduplication | "-230816011_143" |
| ownerId | Wall owner ID; community IDs are negative | -230816011 |
| sourceUrl | Canonical public post link | https://vk.com/wall-230816011_143 |
| text | Visible post text, including line breaks | "Как ни рассказывай..." |
| postedAt | UTC publication time | "2026-09-25T11:05:03.000Z" |
| editedAt | UTC edit time, if exposed | "2026-09-25T11:05:57.000Z" |
| postType | Source post type | "post" |
| isPinned | Whether the wall marks the post as pinned | true |
| author | Public profile or community record | Object |
| author.id | Author ID, negative for a community | -230816011 |
| author.name | Public display name | "Public community" |
| author.type | Profile or community | "community" |
| author.profileUrl | Public author page | https://vk.com/club230816011 |
| author.avatarUrl | Public avatar link, when supplied | Image URL |
| author.isVerified | Source verification flag, when supplied | false |
| engagement | Public interaction and view counters | Object |
| engagement.likes | Likes shown by the source | 2 |
| engagement.comments | Comments count shown by the source | 0 |
| engagement.reposts | Repost count shown by the source | 0 |
| engagement.views | View count shown by the source | 20 |
| engagement.total | Likes plus comments plus reposts | 2 |
| engagement.reactions | Counts by the source reaction ID | Array |
| engagement.reactions\[].type | Reaction ID as text | "0" |
| engagement.reactions\[].count | Count for that reaction | 2 |
| media | Attachments in their original order | Array |
| mediaCount | Number of source attachments | 2 |
| media\[].type | Attachment type, including types without a detailed mapping | "video" |
| media\[].id | Source attachment ID | 456239316 |
| media\[].ownerId | Attachment owner ID | -230816011 |
| media\[].accessKey | Public access key when supplied | String |
| media\[].albumId | Source album ID when supplied | Integer |
| media\[].url | Public attachment or destination link | https://vk.com/video-230816011_456239316 |
| media\[].playerUrl | Public player link when supplied | URL |
| media\[].title | Attachment title | "Video title" |
| media\[].caption | Link caption | String |
| media\[].text | Attachment text | String |
| media\[].description | Attachment description | String |
| media\[].postedAt | Attachment publication time in UTC | "2026-09-25T11:04:28.000Z" |
| media\[].duration | Duration in seconds | 86 |
| media\[].width | Main image width in pixels | 1080 |
| media\[].height | Main image height in pixels | 1920 |
| media\[].thumbnailUrl | Public preview image | URL |
| media\[].variants | Public image sizes supplied by the source | Array |
| media\[].variants\[].url | URL for one image size | URL |
| media\[].variants\[].width | Pixel width for that size | Integer |
| media\[].variants\[].height | Pixel height for that size | Integer |
| media\[].variants\[].type | Source size label when supplied | "x" |
| repost | Direct copied-post context, if present | Object |
| repost.postId | Stable ID of the copied post | String |
| repost.ownerId | Copied post wall owner | Integer |
| repost.sourceUrl | Public copied-post link | URL |
| repost.text | Copied post text | String |
| repost.postedAt | Copied post publication time | UTC timestamp |
| repost.author | Public copied-post author, with the author fields above | Object |
| repost.media | Copied-post attachments, with the media fields above | Array |
| repost.repost | Another copied-post level, if supplied | Object |

The repost chain is limited to three levels. Its nested author and media objects use the same field definitions as the outer post. Attachment arrays can include a simple type value for a source format that has no matching detail fields. This keeps the fact that an attachment existed instead of silently removing it.

The engagement.total field is a convenience sum of likes, comments, and reposts. It does not include views, and reaction counts are already represented in likes. Engagement is a snapshot taken during the run, not a historical series. Run the same wall again later if you need to measure change.

### How to scrape VK

1. Enter one or more public targets. For a community, use its handle, public URL, or negative numeric owner ID. You can also enter a direct post URL when you need a single post.
2. Set maxItems for the whole run and, if collecting several walls, maxItemsPerTarget for each submitted value. The default total cap is 100 posts.
3. Add publishedAfter or publishedBefore if you need a UTC publication-date range. Both bounds include the specified date.
4. Start the run. The actor reads public structured responses and saves each unique eligible post once.
5. Open the default dataset to inspect rows or download JSON, CSV, or Excel. The results output link points to the same dataset.

A direct post link returns at most one row. A wall target can return multiple pages until its public posts are exhausted or a configured limit is reached. The actor checks targets in their submitted order. If two targets lead to the same post, target and targetType reflect the first one that yielded it.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| targets | string array | Required; example \["-230816011"] | Public handles, profile or community URLs, wall URLs, direct post URLs, or numeric owner IDs. |
| maxItems | integer | 100 | Maximum unique rows for the whole run. Set 0 for no total cap. |
| maxItemsPerTarget | integer | 0 | Maximum rows saved for each target. Set 0 for no per-target cap. |
| publishedAfter | date string | Unset | Include posts published on or after this UTC date, YYYY-MM-DD. |
| publishedBefore | date string | Unset | Include posts published on or before this UTC date, YYYY-MM-DD. |
| proxyConfiguration | object | Public proxy enabled | Connection configuration for public requests. |

A realistic wall collection input is:

```json
{
  "targets": ["-230816011"],
  "maxItems": 40
}
```

For a single item, set targets to a public post URL such as https://vk.com/wall-230816011_143 and maxItems to 1. To compare two public walls, put both values in the array and add a per-target cap. Numeric positive IDs refer to profiles; negative IDs refer to communities. A club or public ID also refers to a community.

Date filters apply to the outer wall post. They do not filter a copied post inside repost, because that copied item may have a different publication date. The actor reads older pages to find eligible posts after the requested lower bound. A narrow range on a large wall can therefore involve several public page requests before it saves a row.

### Output example

The following is an excerpt of a real row from a 40-post validation run on 26 September 2026. The long text and attachment details are shortened for readability; author identity is omitted here. The IDs, dates, counts, links, and attachment types are from that run.

```json
{
  "target": "-230816011",
  "targetType": "ownerId",
  "postId": "-230816011_143",
  "ownerId": -230816011,
  "sourceUrl": "https://vk.com/wall-230816011_143",
  "text": "Как ни рассказывай про сварочный инвертор...",
  "postedAt": "2026-09-25T11:05:03.000Z",
  "editedAt": "2026-09-25T11:05:57.000Z",
  "postType": "post",
  "author": {
    "id": -230816011,
    "type": "community"
  },
  "engagement": {
    "likes": 2,
    "comments": 0,
    "reposts": 0,
    "views": 20,
    "total": 2,
    "reactions": [{"type": "0", "count": 2}]
  },
  "media": [
    {
      "type": "video",
      "id": 456239316,
      "ownerId": -230816011,
      "url": "https://vk.com/video-230816011_456239316",
      "postedAt": "2026-09-25T11:04:28.000Z",
      "duration": 86,
      "width": 1080,
      "height": 1920
    },
    {"type": "action_button", "title": "View products", "url": "https://vk.ru/market-230816011"}
  ],
  "mediaCount": 2
}
```

In the validation run, all 40 rows had a post ID, owner ID, source URL, publication time, public author name, and likes count. Thirty-nine had nonempty text; one public post was media-led. Optional edit times, verification flags, and media details depend on what the source exposes for a particular post.

### Use cases

- **Community monitoring:** Save the latest public posts from several communities and compare their publishing cadence, topic mix, and visible engagement.
- **Editorial research:** Collect public post text and source links for a dated period, then review the original page for context before citing it.
- **Campaign measurement:** Take repeated snapshots of likes, comments, reposts, and views on public campaign posts. Use postId to join the snapshots outside the dataset.
- **Media inventory:** Find the public photo, video, clip, link, and other attachment types a community uses. mediaCount and media\[].type support quick grouping.
- **Repost tracing:** Review repost.sourceUrl and its author to identify where public copied content came from, when the source supplies a copy history.
- **Export and audit:** Keep a dated export of public posts for a permitted research or reporting workflow, with canonical links back to each original post.

For monitoring, schedule a bounded run such as 40 or 100 posts per wall at a regular interval. The actor deduplicates within one run. It does not keep a persistent cross-run history, so use postId as the key in your own destination if you combine multiple runs.

### How much does it cost to scrape VK?

The configured price is **$2.88 per 1,000 saved posts**, or $0.00288 for one saved dataset row. The charged event is one public post delivered to the dataset. A skipped, unavailable, or duplicate post does not create a saved row. The run stops when its configured item cap or the caller's event budget is reached.

| Saved posts | Post-event calculation | Post-event price |
| ---: | ---: | ---: |
| 40 | 40 × $0.00288 | $0.1152 |
| 500 | 500 × $0.00288 | $1.44 |
| 5,000 | 5,000 × $0.00288 | $14.40 |

These examples assume that many eligible public posts are available. Setting maxItems to 5,000 is a ceiling; it does not create 5,000 results from a wall with fewer public posts. Platform resource usage and any account-specific charges are shown in the run details. Public access and proxy availability can also affect whether a requested count is reachable.

### Tips for best results

Use a numeric owner ID or a direct wall URL when you already know it. A handle is also supported, but it takes one public identity lookup before the wall can be read. A direct post URL is the most precise choice for one known post. Submitting several aliases for the same wall will not increase the number of unique rows, because duplicate post IDs are saved once per run.

Start with a modest maxItems to check a target's public availability and expected fields. For several walls, set maxItemsPerTarget if you want a balanced sample; otherwise the first wall can fill the whole-run cap. Put the targets you care about most first, since the run processes them in order.

For recent-post monitoring, set publishedAfter to a known UTC date and keep the total cap realistic. The actor pages through the wall from its current public order. Very old ranges on a large wall can require many requests. A pinned older post may appear near the front; date filtering evaluates its actual publication date.

Use sourceUrl to inspect a post in context. The dataset records fields from the public response at run time. View counts and reaction totals may change later, and the source can remove or restrict a post after collection. Keep your own collection date alongside exported data if you need time-series comparisons.

### Limits

This actor collects public wall posts and direct posts. It does not authenticate to a personal account, open private walls, read login-only keyword search results, collect comments as separate rows, or download media files. URLs in media point to public source assets or destinations when supplied. Some attachment formats expose only a type; some video and image URLs can expire or change.

The public response may omit an edit time, pinned flag, verification flag, player link, or author image. A missing optional field is omitted from the row. A post with an image or video can have empty visible text. Counters are the values made public to an anonymous viewer and may differ from counts visible to a signed-in account.

Public access is subject to the site's rate limits and availability. The actor retries temporary rate and server errors, refreshes its anonymous token when needed, and can continue to later independent targets if one target fails. If no public posts can be collected, the run fails rather than presenting an empty dataset as a successful scrape. Pagination has been observed beyond offset 1,000; total reach still depends on what the public endpoint returns for a given wall.

### Use with automated agents

An agent that can start a run and read the dataset can use the input and output schemas directly. Keep the requested target and result limit explicit, then join returned rows on postId when comparing runs.

- “Collect up to 40 recent public posts from owner ID -230816011 and list the ten with the most reposts, including their source links.”
- “Fetch this public wall for posts published since 2026-09-01, export the post IDs and dates, and summarize the attachment types.”

The row contains the original target, stable post identity, canonical link, and the public counters needed for a follow-up workflow. It does not make claims about sentiment, reach, or audience identity.

### FAQ

#### Is it legal to collect these posts?

The actor reads publicly available material. Your intended use still needs to respect applicable law, the site's terms, copyright, privacy rules, and any requirements for personal data. Do not use public profile details to target or misuse individuals.

#### Do I need a VK login or my own token?

No personal login or user token is part of the input. The actor obtains short-lived anonymous public access for each run. If a wall or post requires authentication, it is outside the supported scope.

#### Do I need to configure a proxy?

The default public connection is already configured. Most users can leave proxyConfiguration alone. If the public source is temporarily unavailable from a connection, the run may retry or move to another available proxy route.

#### How fast is a run?

The 40-post validation run completed in about eight seconds. Larger runs take longer because wall pages are requested in batches, especially when a date range requires scanning older pages. Source response time and retries can change the result.

#### Can I schedule monitoring?

Yes. Schedule a run with a bounded input and store its dataset in your destination. Match posts across runs by postId, then compare the saved engagement snapshots. The actor does not itself calculate change across runs.

#### Can I export to a spreadsheet?

Yes. Download the default dataset as CSV or Excel, or send the rows to a spreadsheet integration. Nested author, engagement, media, and repost objects are easiest to preserve in JSON; flatten them in your destination if you need one column per nested value.

#### Why is a field empty or missing?

The public source did not supply that value for the post. Media-only posts can have empty text, while an unedited post may have no editedAt. If a post or wall becomes private or inaccessible, it may yield no row.

### Integrations

The default dataset can be read through the platform API, scheduled runs, webhooks, and supported automation integrations. Use sourceUrl for links to original posts, postId for deduplication, and the nested public fields for downstream reporting. JSON preserves every structured field; CSV and Excel suit tabular summaries after you select the columns you need.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `targets` (type: `array`):

Public handles, profile or community URLs, wall URLs, direct post URLs, or numeric owner IDs. A negative owner ID selects a community.

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

Maximum unique posts to save across all targets. 0 removes this cap.

## `maxItemsPerTarget` (type: `integer`):

Maximum posts to save from each target. 0 removes the per-target cap.

## `publishedAfter` (type: `string`):

Inclusive UTC publication date in YYYY-MM-DD format. Older wall pages are checked until this date is reached.

## `publishedBefore` (type: `string`):

Inclusive UTC publication date in YYYY-MM-DD format.

## `proxyConfiguration` (type: `object`):

Proxy connection for public requests. The default uses datacenter routing.

## Actor input object example

```json
{
  "targets": [
    "-230816011"
  ],
  "maxItems": 100,
  "maxItemsPerTarget": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All collected public posts in the default dataset.

# 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 = {
    "targets": [
        "-230816011"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/vk-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 = { "targets": ["-230816011"] }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/vk-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 '{
  "targets": [
    "-230816011"
  ]
}' |
apify call mlg14/vk-scraper --silent --output-dataset

```

## MCP server setup

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