openapi: 3.0.3
info:
  title: SaveNow Video Download API
  version: "1.0.0"
  description: |
    REST API for downloading YouTube and other supported videos/audio in many
    formats and qualities.

    The SaveNow Video Download REST API lets you submit a video URL, pick an output
    format (audio or video), and retrieve a direct download URL. Long jobs return an
    `id` you can poll via the Progress endpoint until the file is ready.

    ## Authentication

    Every request to the Download endpoint must include your personal `apikey`
    query parameter. Contact us at `sp_golubev@protonmail.com` or visit
    [video-download-api.com](https://video-download-api.com/) to get one.
    The Progress endpoint does not require an API key — only the job `id` returned
    from a previous download call.

    ## Pricing model

    Every successful download deducts credits from your account based on the chosen
    `format`. Video formats beyond the "base duration limit" (e.g. videos longer than
    90 minutes at 1080p) cost extra if `allow_extended_duration=1` is set; otherwise
    those jobs are rejected. See the `ExtendedDuration` schema and the hosted
    documentation page for the full pricing matrix.
  contact:
    name: SaveNow API Support
    email: sp_golubev@protonmail.com
    url: https://video-download-api.com/
  termsOfService: https://video-download-api.com/
  license:
    name: Proprietary
    url: https://video-download-api.com/

servers:
  - url: https://p.savenow.to
    description: Production

tags:
  - name: Download
    description: Submit a new download job and receive a job id.
  - name: Progress
    description: Poll the status of a download job until it finishes.

security:
  - ApiKeyAuth: []

paths:
  /ajax/download.php:
    get:
      tags: [Download]
      summary: Create a download job
      operationId: createDownload
      description: |
        Submits a new download job for the given `url` in the requested `format`.
        Returns a unique `id` you can pass to the Progress endpoint to poll for
        completion and retrieve the final download URL.
      parameters:
        - name: format
          in: query
          required: true
          example: "1080"
          description: |
            The output format to download. Accepted values: `mp3`, `m4a`,
            `webm_audio`, `aac`, `flac`, `opus`, `ogg`, `wav`, `mp2`, `144`,
            `240`, `360`, `480`, `720`, `1080`, `1440`, `4k`, `8k`.

            Audio formats have a 120-minute base duration limit; video formats
            have format-specific limits (1080p=90m, 1440p=60m, 4k/8k=15m,
            everything else=120m).
          schema:
            type: string
        - name: url
          in: query
          required: true
          example: "https://www.youtube.com/watch?v=K1Pu75yJUOE"
          description: The URL-encoded URL of the video to download (YouTube and other supported sources).
          schema:
            type: string
        - name: apikey
          in: query
          required: true
          example: "YOUR_API_KEY"
          description: Your personal API key.
          schema:
            type: string
        - name: add_info
          in: query
          required: false
          example: "1"
          description: Set to `1` to include the `info` object (title + thumbnail) in the response. Defaults to `0` when omitted.
          schema:
            type: integer
        - name: audio_quality
          in: query
          required: false
          example: "128"
          description: Audio bitrate in kbps. Common values include `128`, `192`, `256`, `320`.
          schema:
            type: integer
        - name: allow_extended_duration
          in: query
          required: false
          example: "1"
          description: |
            Set to `1` to permit downloads whose video duration exceeds the format's
            base duration limit. Extended-duration jobs are charged a multiplier of
            the base price (see the `ExtendedDuration` response object). Defaults to
            `0` when omitted.
          schema:
            type: integer
        - name: no_merge
          in: query
          required: false
          example: "0"
          description: |
            If set to `1`, audio and video streams are **not** merged. The client
            receives a zip file containing the separate streams. Defaults to `0`
            (streams are merged into a single file).
          schema:
            type: integer
        - name: audio_language
          in: query
          required: false
          example: "en"
          description: |
            YouTube only. Selects the audio track language by BCP-47 / YouTube
            language code. If the requested track is not available on the video,
            the default track is used instead. No additional cost.

            Supported codes: `af, az, id, ms, bs, ca, cs, da, de, et, en-IN, en-GB,
            en, es, es-419, es-US, eu, fil, fr, fr-CA, gl, hr, zu, is, it, sw, lv,
            lt, hu, nl, no, uz, pl, pt-PT, pt, ro, sq, sk, sl, sr-Latn, fi, sv, vi,
            tr, be, bg, ky, kk, mk, mn, ru, sr, uk, el, hy, iw, ur, ar, fa, ne, mr,
            hi, as, bn, pa, gu, or, ta, te, kn, ml, si, th, lo, my, ka, am, km,
            zh-CN, zh-TW, zh-HK, ja, ko`.
          schema:
            type: string
        - name: start_time
          in: query
          required: false
          example: "30"
          description: |
            Optional. Start time (in seconds) of a cut/segment download. When
            `start_time`/`end_time` are supplied, progress is not reported — the
            Progress endpoint only returns initialization and the final success /
            failure status.
          schema:
            type: integer
        - name: end_time
          in: query
          required: false
          example: "90"
          description: Optional. End time (in seconds) of a cut/segment download.
          schema:
            type: integer
      responses:
        "200":
          description: Job accepted. Use the returned `id` with the Progress endpoint to poll for the final download URL.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DownloadResponse"
              examples:
                standard:
                  summary: Standard download
                  value:
                    success: true
                    id: "abc123xyz789"
                    content: "PGRpdiBjbGFzcz0iZG93bmxvYWQtY29udGVudCI+Li4uPC9kaXY+"
                    info:
                      image: "https://i.ytimg.com/vi/K1Pu75yJUOE/maxresdefault.jpg"
                      title: "Amazing Video Title - 4K Quality"
                extended:
                  summary: Extended-duration download (premium)
                  value:
                    success: true
                    id: "abc123xyz789"
                    content: "PGRpdiBjbGFzcz0iZG93bmxvYWQtY29udGVudCI+Li4uPC9kaXY+"
                    info:
                      image: "https://i.ytimg.com/vi/K1Pu75yJUOE/maxresdefault.jpg"
                      title: "Amazing Video Title - 4K Quality"
                    extended_duration:
                      multiplier: 3
                      original_price: 0.00025
                      final_price: 0.00075
        "400":
          description: Missing or invalid parameters (for example, missing `url`, unknown `format`, or a video that exceeds the base duration without `allow_extended_duration=1`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ajax/progress:
    get:
      tags: [Progress]
      summary: Check download progress
      operationId: getProgress
      security: []
      description: |
        Polls the status of a previously-submitted download job. Keep polling
        until `progress == 1000` (i.e. 100%) and `download_url` is populated.
      parameters:
        - name: id
          in: query
          required: true
          example: "YOUR_JOB_ID"
          description: The job id returned by the Download endpoint.
          schema:
            type: string
      responses:
        "200":
          description: Current progress of the download job.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProgressResponse"
              examples:
                finished:
                  summary: Download finished
                  value:
                    success: 1
                    progress: 1000
                    download_url: "https://shane45.oceansaver.in/pacific/?riPnIjzRoXKtgjjksZmlvBQ"
                    text: "Finished"
                    message: "If you want your application to use our API contact us: sp_golubev@protonmail.com or visit https://video-download-api.com/"
                inProgress:
                  summary: Still downloading
                  value:
                    success: 1
                    progress: 420
                    text: "Downloading"
        "400":
          description: Missing or invalid `id`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: query
      name: apikey
      description: Pass your API key as the `apikey` query parameter on every Download request.

  schemas:
    DownloadResponse:
      type: object
      required: [success, id]
      properties:
        success:
          type: boolean
          description: "`true` when the job was accepted, `false` on error."
        id:
          type: string
          description: Unique identifier for the job. Pass this to the Progress endpoint.
        content:
          type: string
          description: Base64-encoded HTML snippet you can render to show a progress/download UI.
        info:
          $ref: "#/components/schemas/InfoObject"
        extended_duration:
          $ref: "#/components/schemas/ExtendedDuration"

    InfoObject:
      type: object
      description: Extra metadata about the downloaded content. Only present when `add_info=1`.
      properties:
        image:
          type: string
          format: uri
          description: URL of a thumbnail image for the content.
        title:
          type: string
          description: Title of the content.

    ExtendedDuration:
      type: object
      description: |
        Pricing detail returned when `allow_extended_duration=1` **and** the video
        exceeds the format's base duration limit.
      properties:
        multiplier:
          type: integer
          description: Price multiplier applied because of the video's duration (e.g. `3` means 3x pricing).
          example: 3
        original_price:
          type: number
          format: float
          description: Base per-download price for the selected format, before the multiplier.
          example: 0.00025
        final_price:
          type: number
          format: float
          description: Final per-download price after the multiplier is applied.
          example: 0.00075

    ProgressResponse:
      type: object
      required: [success, progress]
      properties:
        success:
          type: integer
          enum: [0, 1]
          description: "`1` on success, `0` on failure."
        progress:
          type: integer
          minimum: 0
          maximum: 1000
          description: Progress in thousandths. `1000` means 100% complete.
        download_url:
          type: string
          format: uri
          description: Direct download URL. Present once the job has finished successfully.
        text:
          type: string
          description: Human-readable status (for example `Downloading`, `Finished`, `Failed`).
        message:
          type: string
          description: Informational message from the API.

    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          description: Human-readable error message.
