> ## Documentation Index
> Fetch the complete documentation index at: https://dev.marginnote.com.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 获取文档页文本

> 按页范围读文本。没有文本层时 MarginNote 会先渲染再识别，因此该接口可能较慢。

单次请求最多跨 60 页。页范围按 PDF 页序编号，第一页为 1。超出范围的页返回空文本，状态码仍为 `200`。

带 `blocks=1` 时返回版面块的规则见参数说明，每块返回文字、类型与页内坐标。
扫描类接口共用两个名额，名额满了会立即返回 `429`，不排队。

新摘录进入全文索引有约十秒延迟，刚摘下来的内容不一定马上搜得到。




## OpenAPI

````yaml /openapi/plugin-api.yaml get /documents/{bookmd5}/pages
openapi: 3.1.0
info:
  title: 场景插件 API
  summary: 场景插件在网页面板里可以调用的接口。
  description: >
    场景插件的面板是一个网页。页面里向 `/bridge/v1` 发请求，就能读写 MarginNote 里的数据。

    请求由 MarginNote 处理，与外部命令行工具共用同一套处理代码。外部工具要带令牌，面板不必带。

    面板页面从 `mnpanel://panel/index.html` 加载，因此 `fetch('/bridge/v1' + path)`
    属于同源请求。


    面板调用受这些条件约束：


    - 请求不带令牌，本文件也不声明安全方案。

    - 页面能调用哪些接口，取决于两份名单。模板里的 `plugins` 名单和沉浸态决定面板能不能建好，
      网页面板白名单决定页面能调哪些接口。不在白名单里的请求返回 `403`，
      错误码为 `IN_PROCESS_NOT_ALLOWED`。
    - `GET /capabilities` 里的 `requiresMax` 只约束外部 Agent。面板的请求在 MarginNote 进程内处理，
      不受令牌、订阅状态与跨设备批准的限制，能否调用只看该路径是否在白名单内。
    - 写操作里的 `addon`，MarginNote 会改写为当前面板所属插件的 id；`confirm` 由 MarginNote 补上。
      页面填的值不会生效。

    用 `mn.api(path, opts)` 读取数据。该方法始终将 Response Body 按 JSON 解析，相当于

    `fetch('/bridge/v1' + path, opts).then(r => r.json())`。


    响应的内容不是 JSON 的接口，改用 `fetch`：


    - `GET /guide` 返回 Markdown 正文。

    - `GET /notes/{noteid}/render` 返回卡片长图或 PDF。

    - `GET /documents/{bookmd5}/pages/{index}/content/{contentId}` 返回图片。

    - `GET /bundles/{bundleId}/files` 带 `?path=` 时返回该文件的原始字节。


    调用失败时 Promise 不会进入 reject。MarginNote 把错误对象

    `{"error":{"code":"…","message":"…"}}` 当作 Response Body 返回，因此结果落在 resolve 里。

    判断调用结果看响应里是否带 `error`，写操作另看响应里的 `persisted`。


    接口清单、各项上限与 Request Body 结构随 MarginNote 的版本变化，运行时以 `GET /capabilities` 与

    `GET /guide` 返回的内容为准。
  version: '0.91'
servers: []
security: []
tags:
  - name: 能力与状态
    description: 读取 MarginNote 的能力、版本与当前上下文。调用其他接口之前先读这组。
  - name: 卡片与摘录
    description: 卡片的读取与写入、摘录的创建、批注、标签、元数据与留白。
  - name: 手写会话
    description: 手写层、作答纸、练习准备清单与会话记录的读取与提交。
  - name: 学习集与卡组
    description: 学习集结构、脑图树、子脑图、色板、快照与复习卡组。
  - name: 文档与页面
    description: 文档目录、页文本、页内注释面、摘录清单与同步诊断。
  - name: 导出与 Anki
    description: 文档导出计划、导出文件、Anki 导出能力、作业与产物。
  - name: 查找
    description: 跨库搜索、文件夹列表与学习集列表。
  - name: 界面与模板
    description: 读取当前界面状态与沉浸模板。
  - name: 插件辅助
    description: 面板辅助脚本、已配对设备与本机 helper 信息。
  - name: 任务包与同步诊断
    description: 拆书任务包与旧 CloudKit 同步诊断。
paths:
  /documents/{bookmd5}/pages:
    get:
      tags:
        - 文档与页面
      summary: 获取文档页文本
      description: |
        按页范围读文本。没有文本层时 MarginNote 会先渲染再识别，因此该接口可能较慢。

        单次请求最多跨 60 页。页范围按 PDF 页序编号，第一页为 1。超出范围的页返回空文本，状态码仍为 `200`。

        带 `blocks=1` 时返回版面块的规则见参数说明，每块返回文字、类型与页内坐标。
        扫描类接口共用两个名额，名额满了会立即返回 `429`，不排队。

        新摘录进入全文索引有约十秒延迟，刚摘下来的内容不一定马上搜得到。
      operationId: getDocumentsBookmd5Pages
      parameters:
        - $ref: '#/components/parameters/Bookmd5'
        - $ref: '#/components/parameters/StartPage'
        - $ref: '#/components/parameters/EndPage'
        - $ref: '#/components/parameters/Blocks'
      responses:
        '200':
          description: 页文本。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagesResponse'
              examples:
                assembled:
                  summary: 构造示例，未实测
                  value:
                    pages:
                      - page: 12
                        text: 把街道还给行人的地方，店面反而活了下来。
                measured:
                  summary: 实测于 2026-10-07，App 0.91
                  value:
                    bookmd5: >-
                      ec280b8dbd77594d16b090165fdd4c07ec280b8dbd77594d16b090165fdd4c07
                    pages:
                      - text: >-
                          'The Quiet Case for Walkable Streets Cities that took
                          space back from cars did not win the argument. They
                          simply stopped having it. Sample material written for
                          the Reading Vocabulary template. Original text, no
                          copyrighted source, not affiliated with any
                          publication. Every weekday morning a city runs an
                          experiment on its own residents, and measures almost
                          nothing about the result. It asks a few hundred
                          thousand people to cross several kilometres inside a
                          narrow window of time, and it judges the outcome by a
                          single number: how long the cars were stationary. The
                          daily commute is the largest recurring transaction
                          between a city and the people who live in it, and for
                          most of the last century we designed it around one
                          assumption, namely that the private car would carry
                          the load. That assumption is now expensive to keep.
                          Not because driving became unpopular, but because the
                          arithmetic never worked. A lane of road moves a fixed
                          number of vehicles per hour no matter how much money
                          is spent on it, and every vehicle that arrives
                          somewhere must be stored there. The real issue is not
                          congestion, which is a symptom. It is that motion and
                          storage compete for the same square metres, and
                          storage always wins, because a parked car occupies its
                          space twenty-two hours a day and pays nothing for the
                          privilege. A plan that nobody would approve today The
                          Dutch city of Groningen offers the clearest
                          counter-example, mostly because it acted before anyone
                          had a vocabulary for what it was doing. The traffic
                          circulation plan proposed in 1977 cut the historic
                          centre into four wedges and made it impossible to
                          drive from one wedge to another without first
                          returning to a ring road. Nothing was banned. Driving
                          to any address remained legal and possible. It was
                          merely made slower than walking, and slower than the
                          bicycle, for the short trips that make up most of an
                          ordinary day. The reaction was what you would expect.
                          Shopkeepers predicted ruin and organised against it;
                          the usual NIMBY objections arrived on schedule from
                          streets that were not even affected. A local paper ran
                          a front page calling the council reckless. Two years
                          later the same shopkeepers asked for the treatment to
                          be extended to their own streets, which is the part of
                          the story that planners tell most often and
                          politicians believe least. '
                        page: 1
                      - text: >-
                          'What makes the case persuasive is not the outcome but
                          the method. The council did not charge drivers a penny
                          to enter the centre, and it did not build anything
                          grand. It changed the order in which existing streets
                          connect to one another, using paint, bollards and a
                          revised set of signs. The budget was trivial. The city
                          worked with the capabilities it already had, rather
                          than the ones it would have needed to buy, and that
                          distinction is why the plan is copyable and a new
                          metro line is not. What travels between cities, and
                          what does not Planners like to point out the
                          similarities between the places that have done this
                          well: a dense pre-war core, a flat surface, a
                          university, a population small enough to cross on foot
                          in half an hour. The list is accurate and slightly
                          misleading, because it invites every other city to
                          conclude that the lesson does not apply to it. Ghent
                          is not flat in the way that matters. Pontevedra had no
                          bicycle culture to lean on. What these places share is
                          not geography but sequencing: each removed the
                          through-traffic first and argued about the parking
                          afterwards. Measurement is where the argument usually
                          goes wrong. Most walkability indices score a
                          neighbourhood by counting what sits within a
                          fifteen-minute radius, which rewards density and
                          punishes everything else. They cannot see whether the
                          pavement is wide enough for two people to pass,
                          whether the crossing gives you eleven seconds or
                          twenty- six, whether the route is pleasant after dark.
                          Those things are decided at a much finer granularity
                          than any index operates at, and they are exactly the
                          things that determine whether a person walks. This is
                          not an argument against cars, and it is worth saying
                          so plainly, because the tone of the debate punishes
                          anyone who forgets to. A city that is easy to cross on
                          foot is also a city where a delivery van finds a kerb,
                          an ambulance finds a clear lane, and the people who
                          genuinely need to drive are not queuing behind the
                          ones who would rather not. The quiet case for walkable
                          streets is not that driving is wrong. It is that a
                          street forced to do every job at once ends up doing
                          all of them badly. Sample article for the Reading
                          Vocabulary template. Written for this purpose; any
                          resemblance to a specific publication''s layout or
                          voice is unintended. '
                        page: 2
                      - text: ''
                        page: 3
                    bridgeApiVersion: '0.91'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
      x-codeSamples:
        - lang: javascript
          label: 获取页文本
          source: >
            const r = await mn.api('/documents/' + bookmd5 + '/pages?start=' +
            page + '&end=' + page);

            if (r.error && r.error.code === 'SCAN_BUSY') {
              // 扫描名额已满，按 details.retryAfterSeconds 等待后重发
              mn.log(r.error.details.retryAfterSeconds);
            }

            mn.log(r.pages[0].text);
components:
  parameters:
    Bookmd5:
      name: bookmd5
      in: path
      required: true
      description: >-
        文档的内容标识。取自 `GET /study-sets/{topicid}/documents` 响应里各项的 `bookmd5`、`GET
        /ui-state` 的 `state.bookmd5`，或 `GET /search?scope=documents`。
      schema:
        type: string
    StartPage:
      name: start
      in: query
      required: true
      description: 起始页，PDF 页序。与结束页一同必填，缺任一项会被拒。
      schema:
        type: integer
        minimum: 1
    EndPage:
      name: end
      in: query
      required: true
      description: 结束页，PDF 页序。与起始页一同必填，单次跨度上限 60 页。
      schema:
        type: integer
        minimum: 1
    Blocks:
      name: blocks
      in: query
      required: false
      description: 切成块返回，每页返回版面检测的块清单。比只取文本慢，建议只取一两页。
      schema:
        type: integer
        enum:
          - 1
  schemas:
    PagesResponse:
      type: object
      properties:
        pages:
          type: array
          items:
            type: object
            properties:
              text:
                type: string
              page:
                type: number
              blocks:
                type: array
                description: 请求带 `blocks=1` 时返回该页的块清单。
                items:
                  type: object
                  properties:
                    index:
                      type: number
                    text:
                      type: string
                    label:
                      type: string
                    rect:
                      $ref: '#/components/schemas/PageRect'
    PageRect:
      type: object
      required:
        - x
        - 'y'
        - width
        - height
      description: PDF 页坐标。
      properties:
        x:
          type: number
        'y':
          type: number
        width:
          type: number
        height:
          type: number
    ErrorEnvelope:
      type: object
      description: >-
        失败时的 Response Body。MarginNote 将其当作正常 Response Body 返回，因此落在 resolve 里而不是
        reject 里。
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorObject'
    ErrorObject:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: 错误码。判断写入是否发生看该字段，不看 HTTP 状态码。
        message:
          type: string
          description: 人可读的说明。文案随 MarginNote 的版本变化，不要用该字段做分支判断。
        retryable:
          type: boolean
          description: >-
            是否明确可以稍后再试。只有外部调用的响应带该字段，面板本地调用的响应只有 `code` 与
            `message`。该布尔值表达不了结果未定，这类情况按各接口的说明处理，例如手写会话族看响应里的
            `layerLeaseUnresolved`。
        details:
          type: object
          description: >-
            附加事实。例如 `SCAN_BUSY` 带 `retryAfterSeconds`，`HASHTAG_ROUTE_REQUIRED` 带
            `writeRoute`。
          additionalProperties: true
  responses:
    TooManyRequests:
      description: 扫描名额已经占满。扫描类接口共享两个名额，超出时立即拒绝，不排队。
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            scanBusy:
              summary: 扫描名额已满
              value:
                error:
                  code: SCAN_BUSY
                  message: 扫描名额已满，按 details.retryAfterSeconds 等后重发。
                  retryable: true
                  details:
                    retryAfterSeconds: 3
    GatewayTimeout:
      description: 扫描迟迟没有结果。
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            scanTimeout:
              summary: 扫描超时
              value:
                error:
                  code: SCAN_TIMEOUT
                  message: 换更小的页范围重发。
                  retryable: true

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.