> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alura.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get sales histories

> Get the monthly sales histories of up to 20 products or shops of other sellers in one call: estimated sales, estimated revenue and reviews written per completed UTC month.

**Cost:** 1 API credit per item returned.

```bash
curl -G "https://api.alura.io/v1/research/history" \
  -H "Authorization: Bearer $ALURA_API_KEY" \
  --data-urlencode "kind=product" \
  --data-urlencode "ids=4524717183,1587263044,1349902215" \
  --data-urlencode "months=12"
```

Needs a plan with full historic data, Growth and above. Other plans get [`403 plan_required`](https://docs.alura.io/errors/plan_required).

## Ask for several histories

| Parameter | Use |
| - | - |
| `kind` | `product` (listing IDs) or `shop` (shop IDs). One kind per call. |
| `ids` | 1 to 20 IDs, comma-separated (`ids=1,2`) or repeated (`ids=1&ids=2`). |
| `months` | Only the last this many completed months of each history, 1 to 120. Default: every month Alura has. |

Every ID comes back once. Its history is in `data`, the same object as [Get product history](https://docs.alura.io/api-reference/research/products/history.md) or [Get shop history](https://docs.alura.io/api-reference/research/shops/history.md) returns. An ID that alone would answer `404` is in `not_found`: Alura hasn't seen it, or doesn't serve it. Nothing is fetched.

## Read the months

Each history holds every completed UTC calendar month, oldest first. The month still running is never included. A month Alura has no values for is listed with `null`, never 0. With `months`, `data_through` names the last month with an estimated sales value within them.

Estimates can change as more data arrives. `methodology_version` and `methodology_url` name the method; see [sales estimates methodology](https://docs.alura.io/methodology/sales-estimates.md).


## OpenAPI

````yaml /openapi.json get /v1/research/history
openapi: 3.1.0
info:
  title: Alura API
  version: 1.0.0
  description: Etsy market research for scripts and AI assistants.
servers:
  - url: https://api.alura.io
security:
  - apiKey: []
paths:
  /v1/research/history:
    get:
      summary: Get sales histories
      description: >-
        Up to 20 Etsy products' (other sellers' listings) or shops' monthly
        sales histories in one call, one `kind` per call. Every ID comes back
        once: its history in `data` (the same object as GET
        /v1/research/products/{id}/history or /shops/{id}/history), or in
        `not_found` when that ID alone would answer 404 (Alura has not seen it,
        or it is not served). `months` keeps only the last that many completed
        months of each history (`data_through` then names the last month with a
        sales value within them). Every completed UTC calendar month of
        estimated sales, estimated revenue (whole USD; for a product, at the
        current price, not the price at the time) and reviews written, oldest
        first, in one response. The month still running is never included; a
        month Alura has no values for is listed with nulls, and `data_through`
        names the last month with an estimated sales value. Carries
        `methodology_version` and `methodology_url`: estimates can change as
        more data arrives. Needs a plan with full historic data (Growth and
        above; otherwise 403 `plan_required`). History never fetches anything: a
        resource Alura has not seen is 404 `not_found`. Costs 1 API credit per
        history returned; IDs in `not_found` are free.


        Cost: 1 API credit per item returned.
      operationId: research.history.getMany
      parameters:
        - name: kind
          in: query
          required: true
          description: >-
            Whose histories: products (by listing ID) or shops (by shop ID); one
            kind per call.
          schema:
            type: string
            enum:
              - product
              - shop
            description: >-
              Whose histories: products (by listing ID) or shops (by shop ID);
              one kind per call.
            example: shop
        - name: ids
          in: query
          required: true
          description: >-
            Etsy listing IDs (`kind=product`) or shop IDs (`kind=shop`),
            comma-separated (`ids=1,2`) or repeated (`ids=1&ids=2`); 1 to 20.
          schema:
            description: >-
              Etsy listing IDs (`kind=product`) or shop IDs (`kind=shop`),
              comma-separated (`ids=1,2`) or repeated (`ids=1&ids=2`); 1 to 20.
            type: array
            items:
              type: string
            example:
              - '48911633'
              - '30577120'
          style: form
          explode: true
        - name: months
          in: query
          required: false
          description: >-
            Only the last this many completed months of each history (1 to 120);
            default every month Alura has.
          schema:
            description: >-
              Only the last this many completed months of each history (1 to
              120); default every month Alura has.
            type: string
            pattern: ^[1-9][0-9]{0,2}$
            example: '12'
        - name: fields
          in: query
          required: false
          description: >-
            Comma-separated dotted paths to return; `id`, `object` and `as_of`
            always come back.
          schema:
            type: string
      responses:
        '200':
          description: OK
          headers:
            Request-Id:
              $ref: '#/components/headers/Request-Id'
            Alura-Credits-Cost:
              $ref: '#/components/headers/Alura-Credits-Cost'
            Alura-Credits-Remaining:
              $ref: '#/components/headers/Alura-Credits-Remaining'
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    const: list
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        object:
                          type: string
                          const: research.history
                          description: Always `research.history`.
                        resource:
                          type: object
                          properties:
                            object:
                              type: string
                              enum:
                                - research.product
                                - research.shop
                            id:
                              type: string
                              description: Etsy ID, as a string.
                          required:
                            - object
                            - id
                          additionalProperties: false
                          description: Whose history this is.
                        granularity:
                          type: string
                          const: month
                          description: 'Always `month`: one entry per calendar month.'
                        history_through:
                          type: string
                          pattern: ^\d{4}-(0[1-9]|1[0-2])$
                          description: >-
                            The last completed UTC month (where `data` ends).
                            The month still running is never included. Not the
                            last month with data: see `data_through`.
                        data_through:
                          anyOf:
                            - type: string
                              pattern: ^\d{4}-(0[1-9]|1[0-2])$
                              description: A UTC calendar month, `YYYY-MM`.
                            - type: 'null'
                          description: >-
                            The last month in `data` with an estimated sales
                            value (`est_sales` not null). A history last
                            calculated months ago ends in null months, so this
                            can be well before `history_through`. null when
                            `data` is null or holds no sales value (unknown, not
                            zero).
                        methodology_version:
                          type: string
                          description: >-
                            The version of the estimate methodology the values
                            follow.
                        methodology_url:
                          type: string
                          description: What the estimates mean, and how accurate they are.
                        data:
                          anyOf:
                            - type: array
                              items:
                                type: object
                                properties:
                                  month:
                                    type: string
                                    pattern: ^\d{4}-(0[1-9]|1[0-2])$
                                    description: A UTC calendar month, `YYYY-MM`.
                                  est_sales:
                                    description: >-
                                      Estimated sales in the month; null when
                                      Alura has no value for it.


                                      Estimate: see the methodology (version
                                      2026-10) at
                                      https://docs.alura.io/methodology/sales-estimates.
                                    type:
                                      - number
                                      - 'null'
                                  est_revenue_usd:
                                    description: >-
                                      Estimated revenue in the month, whole USD;
                                      a product's months are valued at the
                                      current price (not the price at the time).
                                      null when the sales or the price are
                                      unknown.


                                      Estimate: see the methodology (version
                                      2026-10) at
                                      https://docs.alura.io/methodology/sales-estimates.
                                    type:
                                      - number
                                      - 'null'
                                  reviews:
                                    anyOf:
                                      - type: integer
                                      - type: 'null'
                                    description: >-
                                      Reviews written in the month; null when
                                      unknown.
                                required:
                                  - month
                                  - est_sales
                                  - est_revenue_usd
                                  - reviews
                                additionalProperties: false
                            - type: 'null'
                          description: >-
                            Every completed month from the first Alura has
                            through `history_through`, oldest first. A month
                            Alura has no values for is in the list with nulls,
                            never 0 (a history last calculated months ago ends
                            in nulls). null when Alura has no usable history:
                            none stored, no completed month, or months that add
                            up to more than the lifetime total (the product's
                            `est_lifetime`, the shop's Etsy count of sales).
                        unavailable_reason:
                          anyOf:
                            - type: string
                              enum:
                                - not_stored
                                - exceeds_lifetime
                            - type: 'null'
                          description: >-
                            Why `data` is null: `not_stored` (Alura holds no
                            completed month for it) or `exceeds_lifetime` (the
                            stored months add up to more than the lifetime
                            total, so they are not served). null when `data` is
                            present.
                        as_of:
                          type: string
                          description: >-
                            ISO 8601, UTC. When this data was last updated from
                            Etsy.
                      required:
                        - object
                        - resource
                        - granularity
                        - history_through
                        - data_through
                        - methodology_version
                        - methodology_url
                        - data
                        - unavailable_reason
                        - as_of
                      additionalProperties: false
                  pending:
                    type: array
                    items:
                      type: string
                    description: Never-seen IDs being fetched now; ask again later. Free.
                  unfetched:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        code:
                          type: string
                          description: >-
                            Why it was not fetched: `fetch_limit_reached` (a
                            daily fetch cap or a per-minute fetch rate),
                            `fetch_unavailable` (fetching is off, busy or did
                            not finish in time), `service_unavailable` (Alura
                            could not ask for it), `not_fetchable` (Alura does
                            not fetch this one; asking again does not help). New
                            codes may be added.
                        retry_after:
                          type: integer
                          minimum: 1
                          description: Seconds to wait before asking for it again.
                      required:
                        - id
                        - code
                        - retry_after
                      additionalProperties: false
                    description: >-
                      Never-seen IDs that were not fetched, with the reason and
                      when to retry. Free.
                  not_found:
                    type: array
                    items:
                      type: string
                    description: IDs that do not exist on Etsy or are not available. Free.
                required:
                  - object
                  - data
                  - pending
                  - unfetched
                  - not_found
                additionalProperties: false
              example:
                object: list
                data:
                  - object: research.history
                    resource:
                      object: research.shop
                      id: '48911633'
                    granularity: month
                    history_through: 2026-09
                    data_through: 2026-09
                    methodology_version: 2026-10
                    methodology_url: https://docs.alura.io/methodology/sales-estimates
                    data:
                      - month: 2026-07
                        est_sales: 1090
                        est_revenue_usd: 28780
                        reviews: 88
                      - month: 2026-08
                        est_sales: 1150
                        est_revenue_usd: 30360
                        reviews: 92
                      - month: 2026-09
                        est_sales: 1180
                        est_revenue_usd: 31100
                        reviews: 96
                    unavailable_reason: null
                    as_of: '2026-10-13T09:47:00Z'
                pending: []
                unfetched: []
                not_found:
                  - '30577120'
        '400':
          $ref: '#/components/responses/Problem400'
        '401':
          $ref: '#/components/responses/Problem401'
        '402':
          $ref: '#/components/responses/Problem402'
        '403':
          $ref: '#/components/responses/Problem403'
        '404':
          $ref: '#/components/responses/Problem404'
        '405':
          $ref: '#/components/responses/Problem405'
        '429':
          $ref: '#/components/responses/Problem429'
        '500':
          $ref: '#/components/responses/Problem500'
        '503':
          $ref: '#/components/responses/Problem503'
      x-codeSamples:
        - lang: bash
          label: cURL
          source: >-
            curl
            "https://api.alura.io/v1/research/history?kind=shop&ids=48911633,30577120&months=12"
            \
              -H "Authorization: Bearer $ALURA_API_KEY"
        - lang: typescript
          label: TypeScript
          source: >-
            const res = await
            fetch('https://api.alura.io/v1/research/history?kind=shop&ids=48911633,30577120&months=12',
            {
              headers: {
                Authorization: `Bearer ${process.env.ALURA_API_KEY}`,
              },
            });

            console.log(await res.json());
        - lang: python
          label: Python
          source: |-
            import json
            import os
            import urllib.request

            request = urllib.request.Request(
                "https://api.alura.io/v1/research/history?kind=shop&ids=48911633,30577120&months=12",
                headers={
                    "Authorization": f"Bearer {os.environ['ALURA_API_KEY']}",
                },
            )
            with urllib.request.urlopen(request) as response:
                print(json.load(response))
components:
  headers:
    Request-Id:
      description: '`req_` + ULID; quote it when reporting a problem.'
      schema:
        type: string
    Alura-Credits-Cost:
      description: API credits this call cost.
      schema:
        type: integer
    Alura-Credits-Remaining:
      description: >-
        API credits left in the workspace's monthly allowance, counting calls
        still in flight. Absent when it cannot be read right now.
      schema:
        type: integer
  responses:
    Problem400:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/invalid_request
            title: Invalid request
            status: 400
            code: invalid_request
            detail: Invalid request
            suggestion: Fix the request as the detail says, then send it again.
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem401:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/unauthorized
            title: Unauthorized
            status: 401
            code: unauthorized
            detail: Unauthorized
            suggestion: 'Send a valid API key as `Authorization: Bearer alura_sk_...`.'
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem402:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/credits_exhausted
            title: API credits exhausted
            status: 402
            code: credits_exhausted
            detail: API credits exhausted
            suggestion: Upgrade the plan or wait for the monthly reset.
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem403:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/plan_required
            title: Plan required
            status: 403
            code: plan_required
            detail: Plan required
            suggestion: Upgrade the workspace plan to use this operation.
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem404:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/not_found
            title: Not found
            status: 404
            code: not_found
            detail: Not found
            suggestion: Check the ID, or search for it first.
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem405:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/method_not_allowed
            title: Method not allowed
            status: 405
            code: method_not_allowed
            detail: Method not allowed
            suggestion: Use one of the methods in the Allow header.
            retryable: false
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem429:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/rate_limited
            title: Rate limited
            status: 429
            code: rate_limited
            detail: Rate limited
            suggestion: Wait for Retry-After, then retry.
            retryable: true
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem500:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/internal_error
            title: Internal error
            status: 500
            code: internal_error
            detail: Internal error
            suggestion: Retry; if it keeps failing, report the request_id.
            retryable: true
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
    Problem503:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://docs.alura.io/errors/credits_unavailable
            title: API credits unavailable
            status: 503
            code: credits_unavailable
            detail: API credits unavailable
            suggestion: Retry shortly.
            retryable: true
            request_id: req_01JQ5W3H8M2N4P6R8T0V2X4Z6B
  schemas:
    Problem:
      type: object
      description: RFC 9457 problem details.
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
        code:
          type: string
          description: A code from the error catalog; new codes may be added.
          x-extensible-enum:
            - invalid_request
            - unknown_parameter
            - invalid_cursor
            - cursor_window_exceeded
            - invalid_reference
            - id_type_mismatch
            - invalid_keyword
            - shop_id_required
            - listing_not_publishable
            - sales_window_not_covered
            - unauthorized
            - credits_exhausted
            - plan_required
            - insufficient_scope
            - workspace_required
            - workspace_locked
            - your_shop_unavailable
            - writes_suspended
            - browser_refused
            - role_required
            - impersonation_refused
            - recent_sign_in_required
            - etsy_scope_required
            - etsy_partner_required
            - not_found
            - not_collected
            - not_found_on_etsy
            - shop_opted_out
            - method_not_allowed
            - conflict
            - key_limit_reached
            - reconnect_required
            - settings_not_fetched
            - listing_busy
            - settings_busy
            - shop_create_busy
            - idempotency_partial_unverifiable
            - listing_has_variations
            - changed_since
            - change_not_revertable
            - listing_in_ab_test
            - activation_skipped
            - idempotency_processing
            - confirmation_required
            - test_shop_active_cap
            - write_outcome_unknown
            - unprocessable
            - listing_removed_on_etsy
            - endpoint_removed
            - rate_limited
            - throttled
            - daily_credit_cap_exceeded
            - credits_reserved
            - fetch_limit_reached
            - write_rate_limited
            - sync_cooldown
            - shop_write_budget_exhausted
            - internal_error
            - write_misconfigured
            - upstream_unavailable
            - credits_unavailable
            - service_unavailable
            - fetch_unavailable
            - create_lock_unavailable
            - writes_paused
            - etsy_capacity
            - upstream_timeout
        detail:
          type: string
        suggestion:
          type: string
        retryable:
          type: boolean
        request_id:
          type: string
        upgrade_url:
          type: string
          format: uri
          description: 'credits_exhausted and plan_required: where to upgrade the plan.'
        credits_needed:
          type: integer
          description: 'API credit refusals: what the call needed.'
        credits_remaining:
          type: integer
          description: >-
            API credit refusals: what is left (of the month, or of the day for
            daily_credit_cap_exceeded).
        resets_at:
          type: string
          format: date-time
          description: >-
            credits_exhausted and daily_credit_cap_exceeded: when the limit
            resets.
      required:
        - type
        - title
        - status
        - code
        - detail
        - suggestion
        - retryable
        - request_id
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'An API key: `Authorization: Bearer alura_sk_...`.'

````

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