{
  "openapi": "3.0.1",
  "info": {
    "title": "Bilibili Scraper — Videos, Comments, Danmaku & Creator Uploads",
    "description": "Scrape Bilibili (哔哩哔哩), China social media for video: search, video details, comment threads, Chinese influencer uploads and creator profiles (followers, likes), the popular board — plus danmaku (弹幕), the scrolling on-video reactions no other platform has, no cookie needed.",
    "version": "1.3",
    "x-build-id": "Rna1jhyJclqy9ILQ5"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/zhorex~bilibili-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-zhorex-bilibili-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/zhorex~bilibili-scraper/runs": {
      "post": {
        "operationId": "runs-sync-zhorex-bilibili-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/zhorex~bilibili-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-zhorex-bilibili-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "title": "Mode",
            "enum": [
              "search",
              "video_detail",
              "video_comments",
              "user_videos",
              "user_profile",
              "popular"
            ],
            "type": "string",
            "description": "What to scrape from Bilibili",
            "default": "search"
          },
          "searchQuery": {
            "title": "Search query",
            "type": "string",
            "description": "Search query — works in Chinese (人工智能) and English (AI tutorial). Used in 'search' mode."
          },
          "autoLocalize": {
            "title": "Auto-localize brand names (search mode)",
            "type": "boolean",
            "description": "When on, a Latin brand name (e.g. 'Nike') also searches its Chinese name ('耐克') and results are merged — so you get full native recall even when searching in English. Capped at maxResults (no extra cost). Add your own variants via Search aliases below.",
            "default": true
          },
          "searchAliases": {
            "title": "Search aliases (search mode)",
            "type": "array",
            "description": "Extra term variants to also search and merge with your query — e.g. add '耐克' for Nike, or alternate spellings. Useful for brands not in the built-in dictionary.",
            "items": {
              "type": "string"
            }
          },
          "sortOrder": {
            "title": "Sort order (search mode)",
            "enum": [
              "totalrank",
              "click",
              "pubdate",
              "dm",
              "stow",
              "scores"
            ],
            "type": "string",
            "description": "How to sort search results. Used in 'search' mode.",
            "default": "totalrank"
          },
          "durationFilter": {
            "title": "Duration filter (search mode)",
            "enum": [
              "any",
              "short",
              "medium",
              "long",
              "verylong"
            ],
            "type": "string",
            "description": "Filter search results by video duration. Used in 'search' mode.",
            "default": "any"
          },
          "pubtimeBegin": {
            "title": "Published after (search mode)",
            "type": "string",
            "description": "Only videos published on or after this date (or moment). Leave empty for no filter. Used in 'search' mode. Accepted formats: 2026-04-15 (read as 00:00 UTC that day), 2026/04/15, 20260415, 2026-04-15 10:30 or 2026-04-15 10:30:00, and ISO 8601 date-times with or without a zone, e.g. 2026-04-15T10:30:00Z or 2026-04-15T18:30:00+08:00. A time without a zone is read as UTC — add +08:00 for Beijing time. A value the Actor cannot read (e.g. 15-04-2026 or -7d) is NOT applied: the run searches without it and says so in the run status."
          },
          "pubtimeEnd": {
            "title": "Published before (search mode)",
            "type": "string",
            "description": "Only videos published on or before this date (or moment). Leave empty for no filter. Used in 'search' mode. Same formats as 'Published after': 2026-04-15, 2026/04/15, 20260415, 2026-04-15 10:30 or 2026-04-15 10:30:00, and ISO 8601 date-times with or without a zone, e.g. 2026-04-15T10:30:00Z or 2026-04-15T18:30:00+08:00; a time without a zone is read as UTC. A bare date means 00:00 UTC at the START of that day, so 2026-04-15 leaves out videos published later on the 15th — use 2026-04-15T23:59:59Z to include the whole day. A value the Actor cannot read (e.g. 15-04-2026 or -7d) is NOT applied: the run searches without it and says so in the run status."
          },
          "deltaMode": {
            "title": "Delta mode — only NEW videos since last run (search + popular)",
            "type": "boolean",
            "description": "Turn a one-off pull into a recurring monitor: when on, each run returns only videos not seen in previous runs (deduped by video ID across runs, per Delta state key). Pair it with an Apify Schedule for a hands-off daily/weekly 'what's new' feed — you pay only for genuinely new videos. Works in 'search' mode (new videos for your keyword) and 'popular' mode (new videos trending now that weren't before). Tip: in search mode set Sort order to 'Newest first' for monitoring. <b>Know the trade before you switch it on:</b> a full run returns the whole current result set and bills for it; a delta run returns only what has appeared since the last run. On fast-moving boards (trending, hot search, popular) that is still a healthy batch every run. On a narrow keyword or a single creator it can be a handful of rows, or none on a quiet day — which is the feature working, not a broken run. Pick delta when you want to be told what is NEW; leave it off when you want the data itself.",
            "default": false
          },
          "deltaStateKey": {
            "title": "Delta state key (search + popular mode)",
            "type": "string",
            "description": "Names the saved 'already-seen' state so independent monitors don't collide. Use a distinct key per query/category you track (e.g. 'nike-weekly', 'game-trending'). Only used when Delta mode is on.",
            "default": "default"
          },
          "videoUrls": {
            "title": "Video URLs or BVIDs",
            "type": "array",
            "description": "Bilibili video URLs or BV IDs. Used in 'video_detail' and 'video_comments' modes. Example: https://www.bilibili.com/video/BV1xx411c7mD or BV1xx411c7mD",
            "items": {
              "type": "string"
            }
          },
          "userIds": {
            "title": "User IDs (mid)",
            "type": "array",
            "description": "Bilibili user numeric IDs (mid). Used in 'user_profile' and 'user_videos' modes. Find a user's mid in their profile URL: space.bilibili.com/{mid} — pasting that link works too; a channel name does not. <b>In 'user_profile' mode</b> each creator returns ONE row — followers, following, video count, total likes, level, verification and bio — from one request per creator, no search involved; maxResults caps how many profiles a run returns, and an id Bilibili has no profile for is named in the run status and not charged. Schedule it daily or weekly to track follower growth. <b>How 'user_videos' mode works, so the row count is not a surprise:</b> Bilibili's direct space listing is behind risk control that rejects plain HTTP requests, so the Actor searches the creator's channel name and keeps the videos whose mid matches. It reads that search by date first, where the matches are the creator's newest uploads. When other people's videos crowd it (a famous name is on many fans' clips), the rest come from the relevance-ranked search, which favours the creator's best-known videos and can skip recent uploads — the run status says when that happened. Delivered newest first; for a well-known creator expect about 20 videos, not a complete upload list. For a wider sweep use 'search' mode with the creator's name.",
            "items": {
              "type": "string"
            }
          },
          "category": {
            "title": "Category for popular/trending",
            "enum": [
              "all",
              "animation",
              "music",
              "dance",
              "game",
              "knowledge",
              "tech",
              "sports",
              "car",
              "life",
              "food",
              "animal",
              "fashion",
              "entertainment"
            ],
            "type": "string",
            "description": "Filter popular videos by category. Used in 'popular' mode.",
            "default": "all"
          },
          "maxResults": {
            "title": "Max results (raise for bulk monitoring)",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Maximum number of results to scrape. Typical patterns: 20-50 for a quick lookup, 100-300 for daily creator / category monitoring, 500 for a full keyword sweep or content-analytics pull. Higher values automatically walk more result pages. See the Pricing tab for per-result cost.",
            "default": 100
          },
          "includeComments": {
            "title": "Also scrape comments for each video",
            "type": "boolean",
            "description": "In <b>search</b>, <b>user videos</b> and <b>popular</b> modes, also pull the comment thread of every video found and return each comment as its own row (<code>type: comment</code>, with <code>likeCount</code>, <code>replyCount</code> and author). Comments are where the audience opinion is — the video is the prompt, the replies are the sentiment. No login or cookie needed. Cap it with <b>Max comments per video</b>, and note that <b>this multiplies the rows a run returns, and therefore what it costs</b>: 100 videos × 20 comments is ~2,100 rows instead of 100. Off by default.",
            "default": false
          },
          "includeReplies": {
            "title": "Expand comment threads (reply chains)",
            "type": "boolean",
            "description": "Each top-level comment can carry a thread of replies. Turn this on and every reply becomes its own row, tagged with <code>rootRpid</code> and <code>isThreadReply</code> so you can rebuild the conversation. The flag reads the replies Bilibili inlines in the same response AND pages any further ones the root declares in <code>replyCount</code>, one request per thread, stopping as soon as your <code>maxComments</code> is met. <b>How much it adds depends on the video:</b> a popular root measured on 2026-09-16 declared 30 replies and returned 10 on its first page, while quiet roots declare none — and then no extra request is made and no rows are added. <b>Caveats:</b> replies bill as ordinary comment rows, and videos with comments closed return none at all. Off by default.",
            "default": false
          },
          "includeDanmaku": {
            "title": "Add a danmaku profile per video (no cookie needed)",
            "type": "boolean",
            "description": "Danmaku (弹幕) is Bilibili's on-video scrolling commentary — comment text bound to a specific <b>second</b> of the video, which no other platform has. With this on, each video also returns one <code>type: danmaku</code> row with <code>danmakuFetched</code>, <code>danmakuUniqueSenders</code>, <code>danmakuPerMinute</code>, the <b>peak minute</b> and its count, first/last timestamps, and an evenly-spread text sample across the whole video. <b>What it is for:</b> second-by-second audience reaction on an ad or a launch, and creator vetting — danmaku-per-minute beats view count as an engagement signal because a danmaku costs the viewer effort. <b>What is counted:</b> Bilibili serves the danmaku it keeps in the video's pool, and that pool is capped, so on busy videos <code>danmakuFetched</code> is lower than the video's own total. The row says so: <code>danmakuTotal</code> is the video's own danmaku count (single-part videos; null on multi-part uploads) and <code>danmakuPoolCapped</code> is true when fewer were fetched than that total. <b>Cost is predictable:</b> this adds ONE row per video, not one per danmaku. Off by default. <b>Billing:</b> each danmaku row is its own <code>danmaku-profile</code> event at <b>$0.05</b> — 2.5x a normal $0.02 row — because it fetches and reduces the danmaku Bilibili keeps in the video's pool (capped; 3,600 of 4,542 on one measured video). Profiling 200 videos costs $10. Off by default.",
            "default": false
          },
          "danmakuSampleSize": {
            "title": "Danmaku text samples per video",
            "minimum": 0,
            "maximum": 500,
            "type": "integer",
            "description": "How many danmaku texts to include in the sample, spread evenly across the video timeline (not the first N, which would all come from the opening seconds). The sample rides inside the single danmaku row, so raising it does not add billable rows. Set 0 for metrics only (no sample texts).",
            "default": 25
          },
          "maxComments": {
            "title": "Max comments per video",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Maximum number of comments to scrape per video. Used in 'video_comments' mode.",
            "default": 20
          },
          "sortComments": {
            "title": "Sort comments by",
            "enum": [
              "hot",
              "time",
              "likes"
            ],
            "type": "string",
            "description": "How to sort comments. Used in 'video_comments' mode.",
            "default": "hot"
          },
          "sentimentAnalysis": {
            "title": "Sentiment analysis",
            "type": "boolean",
            "description": "Tag each item with lexicon-based Chinese sentiment — polarity + a -1.0…+1.0 score (comments + video titles/descriptions). Optional add-on — for Chinese text it loads a model, so enable with run memory set to 512 MB or more.",
            "default": false
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}