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

# Get Redirects

> Flat form: pixels and geo blocks appear in the card only, so a page of a hundred links does not drag their nested data along.



## OpenAPI

````yaml GET /v1/redirects
openapi: 3.0.0
info:
  title: Teasy API
  description: >-
    Programmatic access to links, landing pages, groups, domains and analytics.


    ## Authentication


    Every request carries an API key in the `Authorization` header:


    ```

    Authorization: Bearer tsl_live_7Kx9mNpQ2rT3vB8dF4hJ6sW1yZ5aC0eG

    ```


    Keys are issued from the dashboard and shown once, at creation. **The key is
    never accepted

    as a query parameter** — query strings end up in proxy logs, browser history
    and `Referer`

    headers, which would leak it.


    The key is a server-side secret. Browser calls are not supported: a key
    shipped in a frontend

    bundle can be read by any visitor.


    ## Rate limits


    Two windows apply per account, both defined by your plan:


    | Header | Meaning |

    |---|---|

    | `RateLimit-Limit-Minute` | requests allowed per minute |

    | `RateLimit-Remaining-Minute` | left in the current minute |

    | `RateLimit-Limit-Day` | requests allowed per day |

    | `RateLimit-Remaining-Day` | left today |


    On `429` the response also carries `Retry-After` — seconds until the window
    that fired

    resets.


    **These headers are absent on `401` and on `429` returned before
    authentication**: with no

    identified account there is no plan and no quota to report. Read them
    defensively — code that

    parses them unconditionally will get `undefined` where it expects a number.


    All API keys of an account share the same budget, so one runaway integration
    affects the rest.


    ## Errors


    Every error, on every status, has the same shape:


    ```json

    {
      "error": {
        "code": "redirect_not_found",
        "message": "Redirect not found.",
        "request_id": "req_9f2c5a4b7d4c31a2f86e0d91b3c7aa10"
      }
    }

    ```


    **The contract is `code`, not `message`.** Messages are written for humans
    and may be

    reworded at any time; codes are stable. Validation failures add a `details`
    array with the

    offending field and a machine code per violation.


    Include `request_id` when contacting support — it is the only way to find
    your exact request

    in our logs.


    | Code | Meaning |

    |---|---|

    | `invalid_request` | Request validation failed. |

    | `invalid_api_key` | API key is missing, invalid or revoked. |

    | `account_suspended` | This account is suspended. |

    | `api_access_not_available_on_current_plan` | The API is not available on
    your current plan. |

    | `feature_not_available_on_current_plan` | This feature is not available on
    your current plan. |

    | `plan_limit_reached` | You have reached the limit of your current plan. |

    | `analytics_range_exceeds_plan_limit` | The requested period is longer than
    your plan allows. |

    | `domain_not_found` | Domain not found. |

    | `domain_not_active` | Domain exists but its verification is not complete
    yet. |

    | `slug_required_for_system_domain` | A slug is required on the system
    domain. |

    | `redirect_not_found` | Redirect not found. |

    | `landing_not_found` | Landing not found. |

    | `group_not_found` | Group not found. |

    | `slug_already_taken` | This address is already taken. |

    | `hostname_already_taken` | This hostname is already connected to another
    account. |

    | `hostname_is_system_domain` | This hostname belongs to the platform and
    cannot be added. |

    | `domain_provider_unavailable` | The domain provider is temporarily
    unavailable. Please retry. |

    | `analytics_not_configured` | This link has no analytics attached, so no
    statistics will ever be available for it. |

    | `payload_too_large` | Request body is too large. |

    | `unsupported_media_type` | Content-Type must be application/json. |

    | `rate_limit_exceeded` | Rate limit exceeded. |

    | `daily_quota_exceeded` | Daily quota exceeded. |

    | `analytics_temporarily_unavailable` | Analytics is temporarily
    unavailable. Please retry. |

    | `internal_error` | Internal server error. |

    | `duplicate_id_in_batch` | This id appears more than once in the batch. |

    | `duplicate_slug_in_batch` | Two items of the batch claim the same address.
    |


    ## Enumerated values grow


    Fields such as widget `type`, traffic `channel` or domain `status` are open
    sets: new values

    appear as the product grows, and that is **not** treated as a breaking
    change. Handle unknown

    values gracefully — an exhaustive `switch` without a default branch will
    break on the day a

    new icon ships.


    ## Pagination


    Lists are cursor-paginated. Pass `pagination.next_cursor` from the previous
    response back as

    `?cursor=`; when `has_more` is `false` there is nothing left to fetch.


    **The cursor is opaque.** It is not a page number, an id or a timestamp — do
    not parse,

    construct or modify it. Its internal format may change without notice, and
    only values we

    returned are guaranteed to work.


    There is no total count: computing it on every request would turn into a
    performance promise

    we would rather spend on serving your data.


    ## Time


    All timestamps in responses are ISO 8601 in UTC (`2026-08-21T10:00:00Z`).


    Analytics endpoints are the one exception on input: period bounds arrive as
    UTC milliseconds

    together with an IANA timezone, which is used to cut buckets in your users'
    local time.


    Two properties of analytics periods worth knowing:


    - both bounds are **inclusive**, so a range ending exactly where the next
    one starts counts an
      event on that edge twice;
    - an `end_date` in the future is silently clamped to the current server
    time. Two identical
      requests can therefore return different numbers — only a period that is entirely in the past
      is stable enough to compare across calls.
  version: '1'
  contact: {}
servers:
  - url: https://api.teasy.link
    description: Production
security: []
tags: []
paths:
  /v1/redirects:
    get:
      tags:
        - Redirects
      summary: List redirects
      description: >-
        Flat form: pixels and geo blocks appear in the card only, so a page of a
        hundred links does not drag their nested data along.
      operationId: ExternalRedirectsController_find_all
      parameters:
        - name: limit
          required: false
          in: query
          description: How many items to return.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: integer
        - name: cursor
          required: false
          in: query
          description: >-
            Value of pagination.next_cursor from the previous page. Opaque —
            pass it back unchanged.
          schema:
            maxLength: 512
            type: string
        - name: group_id
          required: false
          in: query
          description: >-
            Filter by group. Pass "none" for links that belong to no group. An
            unknown group returns an empty list rather than 404.
          schema:
            type: string
            example: grp_1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                      - list
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ExternalRedirect'
                  pagination:
                    $ref: '#/components/schemas/ListPagination'
                required:
                  - object
                  - data
                  - pagination
        '401':
          description: API key is missing, malformed, revoked or unknown.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalErrorResponse'
        '403':
          description: Your plan does not include the API or this particular feature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalErrorResponse'
        '429':
          description: >-
            Rate limit or daily quota exceeded. Retry-After tells you when to
            come back.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalErrorResponse'
        '500':
          description: Unexpected failure on our side.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalErrorResponse'
      security:
        - apiKey: []
components:
  schemas:
    ExternalRedirect:
      type: object
      properties:
        id:
          type: string
          example: rdr_9f2c5a4b7d4c31a2f86e0d91b3c7aa10
        object:
          type: string
          enum:
            - redirect
        name:
          type: string
          example: Promo
        url:
          type: string
          nullable: true
          example: https://teasy.link/promo
          description: >-
            Public address, assembled from the domain and the slug. Null only if
            the link lost its domain.
        target_url:
          type: string
          example: https://onlyfans.com/kate
        slug:
          type: string
          nullable: true
          example: promo
          description: Null when the link occupies the root of a custom domain.
        is_active:
          type: boolean
          example: true
          description: Inactive links stop resolving but keep their statistics.
        note:
          type: string
          nullable: true
          example: Autumn mailing
          description: Private note, never shown to visitors.
        antibot_enabled:
          type: boolean
          example: false
        group_id:
          type: string
          nullable: true
          example: grp_1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
        domain:
          type: string
          nullable: true
          example: teasy.link
          description: >-
            Hostname as a plain string — the same value is passed when creating
            a link.
        created_at:
          type: string
          example: '2026-08-21T10:00:00Z'
          format: date-time
        updated_at:
          type: string
          example: '2026-08-21T10:00:00Z'
          format: date-time
      required:
        - id
        - object
        - name
        - url
        - target_url
        - slug
        - is_active
        - note
        - antibot_enabled
        - group_id
        - domain
        - created_at
        - updated_at
    ListPagination:
      type: object
      properties:
        has_more:
          type: boolean
          example: true
          description: Whether another page exists after this one.
        next_cursor:
          type: string
          nullable: true
          example: eyJjcmVhdGVkX2F0IjoiMjAyNi0wOC0yMVQxMDowMDowMFoifQ
          description: >-
            Pass back as ?cursor= to fetch the next page. Opaque: do not parse
            or construct it. Null on the last page.
      required:
        - has_more
        - next_cursor
    ExternalErrorResponse:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/ExternalError'
      required:
        - error
    ExternalError:
      type: object
      properties:
        code:
          type: string
          enum:
            - invalid_request
            - invalid_api_key
            - account_suspended
            - api_access_not_available_on_current_plan
            - feature_not_available_on_current_plan
            - plan_limit_reached
            - analytics_range_exceeds_plan_limit
            - domain_not_found
            - domain_not_active
            - slug_required_for_system_domain
            - redirect_not_found
            - landing_not_found
            - group_not_found
            - slug_already_taken
            - hostname_already_taken
            - hostname_is_system_domain
            - domain_provider_unavailable
            - analytics_not_configured
            - payload_too_large
            - unsupported_media_type
            - rate_limit_exceeded
            - daily_quota_exceeded
            - analytics_temporarily_unavailable
            - internal_error
            - duplicate_id_in_batch
            - duplicate_slug_in_batch
          description: The contract is this code, not the message.
        message:
          type: string
          example: Redirect not found.
          description: Human-readable and subject to change. Do not match on it.
        request_id:
          type: string
          example: req_9f2c5a4b7d4c31a2f86e0d91b3c7aa10
          description: Quote this when contacting support.
        details:
          description: Present on validation failures, one entry per violation.
          type: array
          items:
            $ref: '#/components/schemas/ExternalErrorDetail'
      required:
        - code
        - message
        - request_id
    ExternalErrorDetail:
      type: object
      properties:
        field:
          type: string
          example: target_url
        code:
          type: string
          example: invalid_value
        message:
          type: string
          example: target_url must be a valid URL
      required:
        - field
        - code
        - message
  securitySchemes:
    apiKey:
      scheme: bearer
      bearerFormat: tsl_live_<32 characters>
      type: http
      description: >-
        API key issued in the dashboard. Server-side only: never ship it to a
        browser, and never pass it as a query parameter.

````