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

# Get seasonal buoy reading series

> Returns grouped year-over-year seasonal time series for a single buoy without pagination.

Supports Météo-France and Candhis buoys and is intended for server-side consumers
that need chart-ready seasonal comparison data. Metric availability is source-specific:
Météo-France supports wave, water temperature, and wind metrics; Candhis supports wave
metrics only. For Candhis, `significient_height` maps to H13D (H1/3),
`period` maps to TH13D (significant wave period), and `peak_period` maps
to TP (peak period).

When `start_year` and `end_year` are omitted, the endpoint returns the last 10 complete
seasons. Explicit year ranges can request up to 13 seasons, for example `2013..2025`.




## OpenAPI

````yaml /api-reference/openapi.json get /buoys/{buoy_id}/readings/series
openapi: 3.0.3
info:
  title: The Buoy API
  version: 2.0.0
  description: >-
    **The Buoy API** — programmatic access to ocean wave buoys on The Surf Kit
    platform.


    All operations are **read-only** (`GET`). Data includes live and historical
    readings, geographic filters, chart time series, and **`GET /conditions`**
    (combined forecast, buoy reading, and tide for a point).


    ## Authentication


    Pass your API key in the `Authorization` header:


    ```

    Authorization: Bearer YOUR_API_KEY

    ```


    `GET /buoys/search` and `GET /buoys/nearest` are public (no key required).


    ## Rate limiting


    Responses include `X-RateLimit-*` headers. Exceeding your hourly quota
    returns `429 Too Many Requests`.
  contact:
    name: API Support
    email: thomas@thebuoy.app
  license:
    name: Proprietary
servers:
  - url: https://api.thebuoy.app/v2
    description: Production server (V2)
  - url: http://localhost:3000/api/v2
    description: Development server (V2)
security:
  - ApiKeyAuth: []
tags:
  - name: Buoys
    description: Real-time ocean buoy data
  - name: Satellite Passes
    description: >-
      Satellite altimeter passes over virtual buoys, and the missions behind
      them
  - name: Conditions
    description: All-in-one conditions endpoint
paths:
  /buoys/{buoy_id}/readings/series:
    get:
      tags:
        - Buoys
      summary: Get seasonal buoy reading series
      description: >
        Returns grouped year-over-year seasonal time series for a single buoy
        without pagination.


        Supports Météo-France and Candhis buoys and is intended for server-side
        consumers

        that need chart-ready seasonal comparison data. Metric availability is
        source-specific:

        Météo-France supports wave, water temperature, and wind metrics; Candhis
        supports wave

        metrics only. For Candhis, `significient_height` maps to H13D (H1/3),

        `period` maps to TH13D (significant wave period), and `peak_period` maps

        to TP (peak period).


        When `start_year` and `end_year` are omitted, the endpoint returns the
        last 10 complete

        seasons. Explicit year ranges can request up to 13 seasons, for example
        `2013..2025`.
      operationId: getBuoyReadingSeries
      parameters:
        - name: buoy_id
          in: path
          required: true
          schema:
            type: integer
        - name: metric
          in: query
          required: true
          description: >-
            Metric to return for the seasonal series. Candhis supports
            `significient_height`, `maximum_height`, `period`, `peak_period`,
            and `direction`.
          schema:
            type: string
            enum:
              - significient_height
              - maximum_height
              - period
              - peak_period
              - direction
              - water_temperature
              - wind_speed
              - wind_direction
              - gust_speed
        - name: bucket
          in: query
          description: Bucket size for grouping the seasonal series
          schema:
            type: string
            enum:
              - hour
              - day
              - week
            default: hour
        - name: aggregation
          in: query
          description: Aggregation used when bucket is day or week
          schema:
            type: string
            enum:
              - median
              - avg
              - min
              - max
            default: median
        - name: start_year
          in: query
          description: >-
            Inclusive first season year. Explicit ranges can span up to 13
            seasons.
          schema:
            type: integer
        - name: end_year
          in: query
          description: >-
            Inclusive last season year. Explicit ranges can span up to 13
            seasons.
          schema:
            type: integer
        - name: season_start_month
          in: query
          description: Season start month
          schema:
            type: integer
            default: 4
            minimum: 1
            maximum: 12
        - name: season_start_day
          in: query
          description: Season start day of month
          schema:
            type: integer
            default: 1
            minimum: 1
            maximum: 31
        - name: season_end_month
          in: query
          description: Season end month
          schema:
            type: integer
            default: 6
            minimum: 1
            maximum: 12
        - name: season_end_day
          in: query
          description: Season end day of month
          schema:
            type: integer
            default: 30
            minimum: 1
            maximum: 31
        - name: timezone
          in: query
          description: >-
            IANA timezone used for bucket boundaries and labels. Defaults to the
            buoy timezone or UTC.
          schema:
            type: string
            example: Europe/Paris
      responses:
        '200':
          description: Seasonal chart series grouped by year
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: object
                    properties:
                      buoy:
                        type: object
                        properties:
                          id:
                            type: integer
                          name:
                            type: string
                          lat:
                            type: number
                            format: float
                          lng:
                            type: number
                            format: float
                          source:
                            type: string
                          dtz:
                            type: string
                            nullable: true
                      metric:
                        type: string
                        enum:
                          - significient_height
                          - maximum_height
                          - period
                          - peak_period
                          - direction
                          - water_temperature
                          - wind_speed
                          - wind_direction
                          - gust_speed
                      bucket:
                        type: string
                        enum:
                          - hour
                          - day
                          - week
                      aggregation:
                        type: string
                        nullable: true
                        enum:
                          - median
                          - avg
                          - min
                          - max
                      timezone:
                        type: string
                      season:
                        $ref: '#/components/schemas/SeasonRange'
                      years:
                        type: array
                        items:
                          $ref: '#/components/schemas/BuoySeasonalSeriesYear'
                      count:
                        type: integer
                  meta:
                    type: object
                    description: Non-paginated response metadata
              example:
                status: success
                data:
                  buoy:
                    id: 295
                    name: Azur
                    lat: 43.38
                    lng: 7.83
                    source: Météo France
                    dtz: Europe/Paris
                  metric: significient_height
                  bucket: day
                  aggregation: median
                  timezone: Europe/Paris
                  season:
                    start:
                      month: 4
                      day: 1
                    end:
                      month: 6
                      day: 30
                  years:
                    - year: 2025
                      complete: true
                      point_count: 91
                      points:
                        - bucket_index: 1
                          label: Apr 01
                          bucket_start: '2025-04-01T00:00:00+02:00'
                          bucket_end: '2025-04-01T23:59:59+02:00'
                          value: 1.12
                          sample_count: 24
                  count: 91
                meta: {}
        '400':
          $ref: '#/components/responses/BadRequest'
          description: Invalid metric, bucket, aggregation, timezone, or season window
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          description: Unsupported buoy source for seasonal series
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
      security:
        - ApiKeyAuth: []
components:
  headers:
    XRateLimitLimit:
      schema:
        type: integer
        description: Maximum requests allowed per hour
        example: 1000
      description: Maximum requests allowed per hour
    XRateLimitRemaining:
      schema:
        type: integer
        description: Requests remaining in current hour
        example: 987
      description: Requests remaining in current hour
    XRateLimitReset:
      schema:
        type: integer
        description: Unix timestamp when rate limit resets
        example: 1735326000
      description: Unix timestamp when rate limit resets
  schemas:
    SeasonRange:
      type: object
      properties:
        start:
          type: object
          properties:
            month:
              type: integer
            day:
              type: integer
        end:
          type: object
          properties:
            month:
              type: integer
            day:
              type: integer
    BuoySeasonalSeriesYear:
      type: object
      properties:
        year:
          type: integer
        complete:
          type: boolean
          description: Whether the requested season is complete for this year
        point_count:
          type: integer
        points:
          type: array
          items:
            $ref: '#/components/schemas/BuoySeasonalSeriesPoint'
    UnprocessableEntity:
      type: object
      description: Validation error response (422)
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          properties:
            details:
              type: object
              additionalProperties: true
    BuoySeasonalSeriesPoint:
      type: object
      properties:
        bucket_index:
          type: integer
          description: Season-relative bucket index
        label:
          type: string
          description: Display label for the bucket
        bucket_start:
          type: string
          format: date-time
        bucket_end:
          type: string
          format: date-time
        value:
          type: number
          format: float
          nullable: true
        sample_count:
          type: integer
          description: Number of raw readings contributing to the bucket
    ErrorResponse:
      type: object
      description: Standardized error response format for V2 API
      properties:
        error:
          type: string
          description: >-
            Error code (e.g., "unauthorized", "bad_request",
            "resource_not_found")
          example: unauthorized
        message:
          type: string
          description: Human-readable error message
          example: Invalid API key
        details:
          type: object
          description: Additional error details (optional, varies by error type)
  responses:
    BadRequest:
      description: Bad request (400 Bad Request)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: bad_request
            message: Search term must be at least 2 characters
            timestamp: '2025-12-27T14:30:00Z'
            request_id: 550e8400-e29b-41d4-a716-446655440000
    Unauthorized:
      description: Invalid or missing API key (401 Unauthorized)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: unauthorized
            message: Invalid API key
            timestamp: '2025-12-27T14:30:00Z'
            request_id: 550e8400-e29b-41d4-a716-446655440000
    RateLimitExceeded:
      description: Rate limit exceeded (429 Too Many Requests)
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
            description: Maximum requests allowed per hour
            example: 1000
        X-RateLimit-Remaining:
          schema:
            type: integer
            description: Requests remaining in current hour
            example: 0
        X-RateLimit-Reset:
          schema:
            type: integer
            description: Unix timestamp when rate limit resets (top of next hour)
            example: 1735326000
        Retry-After:
          schema:
            type: integer
            description: Seconds to wait before retrying
            example: 3600
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: rate_limit_exceeded
            message: You have exceeded 1000 requests per hour
            timestamp: '2025-12-27T14:30:00Z'
            request_id: 550e8400-e29b-41d4-a716-446655440000
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >
        API key authentication. Pass your API key as a Bearer token in the
        Authorization header.


        Format: `Authorization: Bearer YOUR_API_KEY`


        Alternative: Pass as query parameter `?api_key=YOUR_API_KEY`


        **Security:** API keys are stored as BCrypt hashes (never plain text).
        Only the hash is stored in the database.

````