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

# Look up posts for a handle, hashtag, or other term

> Returns recent posts for the given term across the requested platforms.
Availability and coverage vary by platform; results are returned on a
best-effort basis.

**Term type — explicit or auto-detected.** Pass `term_type` to choose how
the term is interpreted. If omitted, it is auto-detected:
- Terms starting with `#` → `hashtag` (e.g. `#travel`)
- Terms starting with `@` or with no prefix → `username` (e.g. `@nasa` or `nasa`)

Some platforms only accept term types that can't be auto-detected and so
**require** an explicit `term_type` — e.g. Reddit (`channel` for a
subreddit, `mentions` for a keyword search). See the `term_type` parameter
for the supported value per platform.

**Supported platforms and term types** (this endpoint serves only
connection-free lookups, a subset of `GET /platforms`):

| Platform | term_type values |
| --- | --- |
| Bluesky | `username`, `hashtag` |
| Twitter (X) | `username`, `hashtag`, `mentions` |
| YouTube | `username`, `channel`, `hashtag` |
| Vimeo | `username` |
| Facebook | `username`, `hashtag` |
| Instagram | `username`, `hashtag` |
| Tumblr | `username`, `hashtag` |
| Pinterest | `username`, `hashtag` |
| Flickr | `username` |
| Giphy | `username` |
| LinkedIn | `username`, `hashtag` |
| Reddit | `channel`, `mentions` |
| TikTok | `username`, `hashtag`, `mentions` |

A platform not listed here, or an unsupported `term_type` for a listed
platform, comes back in `meta.platforms` with an error — other platforms
in the same request still return.

**Pagination.** Each call returns one upstream page per platform (the page
size is whatever the platform's API returns — it varies by platform and is
not configurable; there is no `limit`/`per_page` param). Every successful
`meta.platforms[]` entry includes a `next_cursor` and `has_more`. To fetch
the next page, repeat the same request (same `term`/`platforms`/`term_type`)
and add the `cursor` param with the `next_cursor` value(s) you received.
Pagination is per-platform: in a multi-platform request, pass a
comma-separated list of the cursors for the platforms you want to continue;
platforms whose `next_cursor` was `null` have no more results. Cursors are
opaque — do not parse or construct them.




## OpenAPI

````yaml /openapi/v1.yaml get /data/posts
openapi: 3.1.0
info:
  title: Juicer API v1
  version: 1.0.0
  description: >
    The Juicer API provides two products under one API key:


    1. **Integration API** — Programmatically manage feeds, sources, posts, and
    moderation

    2. **Data API** — Query social posts for a handle or hashtag without
    creating a feed


    ## Authentication

    All requests require a Bearer token in the Authorization header:

    ```

    Authorization: Bearer jcr_your_api_key_here

    ```

    API keys can be created from the Developer page in the Juicer dashboard,

    **or** programmatically via `POST /v1/authorize` — see step 0 of the

    Quick Start below. No prior signup or dashboard access is required.


    ## Quick Start


    Create a feed, add a source, and get the embed code:


    ```bash

    # 0. Get an API key (no dashboard needed)

    curl -X POST https://api.juicer.io/v1/authorize \
      -H "Content-Type: application/json" \
      -d '{"email": "you@example.com", "client_name": "My Tool"}'
    # New email → returns `api_key` immediately (temporary until email
    confirmed).

    # Existing user → returns an `authorization_url` (user approves) and a

    # `poll_url`; GET the poll_url until it returns the `api_key`.


    # 1. Check your account

    curl https://api.juicer.io/v1/account \
      -H "Authorization: Bearer jcr_your_key"

    # 2. See available platforms

    curl https://api.juicer.io/v1/platforms \
      -H "Authorization: Bearer jcr_your_key"

    # 3. Create a feed

    curl -X POST https://api.juicer.io/v1/feeds \
      -H "Authorization: Bearer jcr_your_key" \
      -H "Content-Type: application/json" \
      -d '{"name": "My Brand Feed"}'

    # 4. Add a YouTube source

    curl -X POST https://api.juicer.io/v1/feeds/{feed_id}/sources \
      -H "Authorization: Bearer jcr_your_key" \
      -H "Content-Type: application/json" \
      -d '{"platform": "YouTube", "term": "NASA", "term_type": "username"}'

    # 5. Get embed code for your website

    curl https://api.juicer.io/v1/feeds/{feed_id}/embed \
      -H "Authorization: Bearer jcr_your_key"
    ```


    The embed endpoint returns ready-to-use snippets:


    - **JavaScript** — `<script>` tag for any website

    - **iframe** — For platforms that don't allow JavaScript

    - **WordPress shortcode** — `[juicer name='your-feed']`

    - **WordPress PHP** — `<?php juicer_feed('name=your-feed'); ?>`


    ## Discovering Platforms


    Use `GET /platforms` to see all available social media platforms and their

    supported term types (username, hashtag, mentions, etc.) before creating
    sources.

    Each term type includes its OAuth connection requirements, so you know
    upfront

    whether the user needs to connect an account first.


    ## Connecting Social Accounts


    Several platforms require a connected social account before you can create

    sources. The exact list and the per-term-type requirement depend on the

    caller's account configuration — always discover requirements at runtime

    from `GET /platforms` (the `requires_connection` field on each term type)

    or `GET /social_accounts/status`. The flow when a connection is needed:


    1. `GET /social_accounts/status` — check which platforms need connecting

    2. `POST /social_accounts/connect_url` with `{"provider": "facebook"}` —
    generates a magic link

    3. User clicks the link — it signs them in and starts the authorization flow
    (no Juicer login needed)

    4. `GET /social_accounts` — verify the account is now connected

    5. Create sources for that platform as normal


    The magic link expires in 30 minutes and can only be used once. This makes

    it suitable for AI agents and CLI tools that need to walk a user through

    social account setup.


    **Note:** If you try to create a source that requires a connected account

    and none is available, the API returns a `422` error with

    `error.code = "social_account_required"` and an `error.action` containing

    a magic link the user can click to complete the connection.


    ## Plan Limits


    Your plan determines how many feeds and sources you can create. If you hit

    a limit, the API returns a `422` error with `error.code =
    "feed_limit_reached"`

    or `"source_limit_reached"`, along with an `error.action` of type
    `"upgrade_plan"`

    that includes a magic link to the upgrade page with the correct plan
    pre-selected.
servers:
  - url: https://api.juicer.io/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Quickstart
    description: Get an API key with just an email, no dashboard required
  - name: Account
    description: Account info, usage, and limits
  - name: Platforms
    description: Discover available platforms and term types
  - name: Social Accounts
    description: Manage OAuth social account connections needed for certain platforms
  - name: Feeds
    description: Manage feeds (create, update, delete, list)
  - name: Sources
    description: Manage sources within feeds
  - name: Posts
    description: Read, search, and moderate posts
  - name: Embed
    description: Get embed code snippets for your website
  - name: Search
    description: Search posts across all feeds
  - name: Analytics
    description: Feed engagement analytics and performance
  - name: Webhooks
    description: Webhook subscriptions and event delivery
  - name: Data API
    description: One-off post lookups for a handle or hashtag without creating a feed
paths:
  /data/posts:
    get:
      tags:
        - Data API
      summary: Look up posts for a handle, hashtag, or other term
      description: >
        Returns recent posts for the given term across the requested platforms.

        Availability and coverage vary by platform; results are returned on a

        best-effort basis.


        **Term type — explicit or auto-detected.** Pass `term_type` to choose
        how

        the term is interpreted. If omitted, it is auto-detected:

        - Terms starting with `#` → `hashtag` (e.g. `#travel`)

        - Terms starting with `@` or with no prefix → `username` (e.g. `@nasa`
        or `nasa`)


        Some platforms only accept term types that can't be auto-detected and so

        **require** an explicit `term_type` — e.g. Reddit (`channel` for a

        subreddit, `mentions` for a keyword search). See the `term_type`
        parameter

        for the supported value per platform.


        **Supported platforms and term types** (this endpoint serves only

        connection-free lookups, a subset of `GET /platforms`):


        | Platform | term_type values |

        | --- | --- |

        | Bluesky | `username`, `hashtag` |

        | Twitter (X) | `username`, `hashtag`, `mentions` |

        | YouTube | `username`, `channel`, `hashtag` |

        | Vimeo | `username` |

        | Facebook | `username`, `hashtag` |

        | Instagram | `username`, `hashtag` |

        | Tumblr | `username`, `hashtag` |

        | Pinterest | `username`, `hashtag` |

        | Flickr | `username` |

        | Giphy | `username` |

        | LinkedIn | `username`, `hashtag` |

        | Reddit | `channel`, `mentions` |

        | TikTok | `username`, `hashtag`, `mentions` |


        A platform not listed here, or an unsupported `term_type` for a listed

        platform, comes back in `meta.platforms` with an error — other platforms

        in the same request still return.


        **Pagination.** Each call returns one upstream page per platform (the
        page

        size is whatever the platform's API returns — it varies by platform and
        is

        not configurable; there is no `limit`/`per_page` param). Every
        successful

        `meta.platforms[]` entry includes a `next_cursor` and `has_more`. To
        fetch

        the next page, repeat the same request (same
        `term`/`platforms`/`term_type`)

        and add the `cursor` param with the `next_cursor` value(s) you received.

        Pagination is per-platform: in a multi-platform request, pass a

        comma-separated list of the cursors for the platforms you want to
        continue;

        platforms whose `next_cursor` was `null` have no more results. Cursors
        are

        opaque — do not parse or construct them.
      operationId: getDataPosts
      parameters:
        - name: term
          in: query
          required: true
          description: >-
            The term to look up (handle, hashtag, or keyword), interpreted per
            `term_type`
          schema:
            type: string
            example: '@nasa'
        - name: platforms
          in: query
          required: true
          description: |
            Comma-separated list of platforms to query. See the table above for
            the platforms this endpoint supports.
          schema:
            type: string
            example: Instagram,YouTube
        - name: term_type
          in: query
          required: false
          description: |
            How to interpret `term`. When omitted it is auto-detected
            (`#`-prefixed → `hashtag`, otherwise `username`). Applied to every
            requested platform; a platform that doesn't support the given value
            returns an `invalid_term_type` error in `meta.platforms`. See the
            table above for valid values per platform.
          schema:
            type: string
            enum:
              - username
              - hashtag
              - channel
              - mentions
            example: channel
        - name: cursor
          in: query
          required: false
          description: >
            Opaque pagination cursor(s) from a prior response's

            `meta.platforms[].next_cursor`. Comma-separate multiple cursors to

            continue several platforms at once. Each cursor encodes its own

            platform; cursors are matched to the platforms named in `platforms`,

            and a malformed cursor is reported as an `invalid_cursor` entry in

            `meta.platforms` without affecting the others. Omit on the first
            page.
          schema:
            type: string
      responses:
        '200':
          description: Posts fetched successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataPostsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
components:
  schemas:
    DataPostsResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Post'
        meta:
          type: object
          required:
            - total_results
            - platforms
          properties:
            total_results:
              type: integer
            platforms:
              type: array
              items:
                $ref: '#/components/schemas/PlatformStatus'
    Post:
      type: object
      description: Post returned by the Data API
      properties:
        platform:
          type: string
        platform_id:
          type: string
          nullable: true
        url:
          type: string
          nullable: true
        post_created_at:
          type: string
          format: date-time
          nullable: true
        message:
          type: string
          nullable: true
        like_count:
          type: integer
          description: >
            Like/reaction count. Present for every platform that has a concept
            of engagement. Omitted entirely for platforms that report none
            (RSS/blog feeds, Google reviews).
        comment_count:
          type: integer
          description: >
            Comment/reply count. Present for every platform that has a concept
            of engagement. Omitted entirely for platforms that report none
            (RSS/blog feeds, Google reviews).
        share_count:
          type: integer
          description: >
            Public share/repost count. Present only for platforms that collect
            it (Facebook, TikTok, X, Bluesky). Omitted entirely for other
            platforms.
        view_count:
          type: integer
          description: >
            Video view count. Present only for YouTube and TikTok. Omitted for
            platforms that don't collect it.
        quote_count:
          type: integer
          description: >
            Number of times the post has been quoted. Present only for X and
            Bluesky. Omitted for platforms that don't collect it.
        bookmark_count:
          type: integer
          description: >
            Bookmark count. Present only for X. Omitted for platforms that don't
            collect it.
        impression_count:
          type: integer
          description: >
            Impression count. Present only for X. Omitted for platforms that
            don't collect it.
        tagged_users:
          type: string
        poster:
          $ref: '#/components/schemas/Poster'
        media:
          type: array
          items:
            $ref: '#/components/schemas/Medium'
        referenced_post:
          $ref: '#/components/schemas/ReferencedPost'
          nullable: true
        reshared_by:
          $ref: '#/components/schemas/Poster'
          nullable: true
    PlatformStatus:
      type: object
      required:
        - platform
        - success
      properties:
        platform:
          type: string
        success:
          type: boolean
        count:
          type: integer
          description: >-
            Number of posts returned for this platform on this page (present on
            success).
        next_cursor:
          type: string
          nullable: true
          description: >
            Opaque cursor for the next page of this platform's results. Pass it
            back

            in the `cursor` query param to continue. `null` when there are no
            more

            results (or the platform does not support pagination, e.g.
            Pinterest).
        has_more:
          type: boolean
          description: True when `next_cursor` is present (more results are available).
        error_code:
          type: string
          enum:
            - unknown_platform
            - invalid_term_type
            - fetch_timeout
            - invalid_term
            - platform_error
            - social_account_required
            - invalid_cursor
        message:
          type: string
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: >
                Machine-readable error code. Possible values:

                - `unauthorized` — Missing or invalid API key

                - `forbidden` — API not enabled for account

                - `not_found` — Resource not found

                - `parameter_missing` — Required parameter not provided

                - `validation_failed` — Model validation failed (see `details`)

                - `feed_limit_reached` — Plan's feed limit exceeded (see
                `action`)

                - `source_limit_reached` — Feed's source limit exceeded (see
                `action`)

                - `user_limit_reached` — Plan doesn't include team members (see
                `action`)

                - `social_account_required` — OAuth connection needed (see
                `action`)

                - `invalid_platform` — Platform not supported

                - `invalid_provider` — Unknown OAuth provider

                - `invalid_status` — Invalid post status filter

                - `invalid_action_type` — Invalid bulk moderation action

                - `query_required` — Search query not provided
              example: feed_limit_reached
            message:
              type: string
              description: Human-readable error message
              example: Your Free plan allows 1 feed(s). Upgrade to create more.
            details:
              type: object
              nullable: true
              description: >-
                Field-level validation errors. Present when `code` is
                `validation_failed`.
              additionalProperties:
                type: array
                items:
                  type: string
              example:
                name:
                  - can't be blank
            action:
              nullable: true
              description: >
                Actionable fix for the error. Present when the error can be
                resolved

                by the user taking a specific action (upgrading plan or
                connecting a social account).

                The `type` field tells you what kind of action is needed.
              oneOf:
                - $ref: '#/components/schemas/UpgradePlanAction'
                - $ref: '#/components/schemas/ConnectSocialAccountAction'
    Poster:
      type: object
      properties:
        display_name:
          type: string
        name:
          type: string
        url:
          type: string
          nullable: true
        image:
          type: string
          nullable: true
        platform_id:
          type: string
          nullable: true
    Medium:
      type: object
      properties:
        type:
          type: string
          enum:
            - image
            - video
        url:
          type: string
        preview_image_url:
          type: string
          nullable: true
        width:
          type: integer
          nullable: true
        height:
          type: integer
          nullable: true
        alt_text:
          type: string
          nullable: true
        platform_id:
          type: string
          nullable: true
    ReferencedPost:
      type: object
      properties:
        poster:
          $ref: '#/components/schemas/Poster'
        message:
          type: string
          nullable: true
        url:
          type: string
          nullable: true
        platform_id:
          type: string
          nullable: true
        post_created_at:
          type: string
          format: date-time
          nullable: true
        media:
          type: array
          items:
            $ref: '#/components/schemas/Medium'
    UpgradePlanAction:
      type: object
      description: >
        Returned when a plan limit is hit (feed limit, source limit).

        The `upgrade_url` is a magic link — the user clicks it, gets signed in

        automatically, and lands on the upgrade page with the right plan
        pre-selected.
      properties:
        type:
          type: string
          enum:
            - upgrade_plan
        current_plan:
          type: string
          example: small
        current_plan_display_name:
          type: string
          example: Free
        required_plan:
          type: string
          description: The minimum plan that unlocks this feature
          example: large
        required_plan_display_name:
          type: string
          example: Pro
        upgrade_url:
          type: string
          description: >-
            Magic link — signs user in and redirects to the upgrade page.
            Expires in 30 minutes.
        expires_at:
          type: string
          format: date-time
    ConnectSocialAccountAction:
      type: object
      description: >
        Returned when a source requires an OAuth-connected social account.

        The `connection_url` is a magic link — the user clicks it, gets signed
        in

        automatically, and starts the OAuth authorization flow.
      properties:
        type:
          type: string
          enum:
            - connect_social_account
        provider:
          type: string
          description: OAuth provider to connect
          example: facebook
        connection_url:
          type: string
          description: >-
            Magic link — signs user in and starts OAuth flow. Expires in 30
            minutes.
        expires_at:
          type: string
          format: date-time
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableEntity:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        API key prefixed with `jcr_`. To obtain a key programmatically
        (no dashboard required), see `POST /v1/authorize`.

````