vulk-dev Projects API

Start, continue, list, read and delete projects; read their delivered files.

Operations 6

GET /projects List projects #
POST /projects Create a project from a brief #
GET /projects/{id} Read a project #
DELETE /projects/{id} Delete a project #
GET /projects/{id}/files Read the delivered files #
POST /projects/{id}/messages Send the next instruction #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/vulk-dev:vulk-dev-projects-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

vulk-dev-projects-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: VULK REST Projects API
  summary: Build, continue and read VULK projects from any program or AI agent.
  description: 'The VULK REST API drives the VULK generation engine with the same plan and

    credits as the editor: start a project from a brief, send it the next

    instruction, follow the operation, read the delivered files, the models

    your account may use and your usage.


    The API is included in every VULK plan. Keys are created in Settings › API

    keys at https://app.vulk.dev/settings?tab=ApiKeys and sent as

    `Authorization: Bearer vk_sk_`. Each key belongs to one account, carries

    the scopes chosen when it was created (projects.read, projects.write,

    export.read, usage.read), may make 60 requests per minute and can be revoked

    at any time.


    Reads never start a generation and never spend credits. POST /projects and

    POST /projects/{id}/messages spend the credits of your plan, as the same brief

    typed into the editor would. Request bodies are JSON of at most 64 KB;

    answers are JSON in camelCase with ISO 8601 dates in UTC. GET /api/v1 answers

    without a key with the list of endpoints.


    Reference: https://support.vulk.dev/docs/api. Quickstart and MCP setup:

    https://vulk.dev/developers.'
  version: 2.0.0
  contact:
    name: VULK Support
    url: https://vulk.dev/contact
    email: support@vulk.dev
  termsOfService: https://vulk.dev/terms-conditions
  license:
    name: Proprietary
    url: https://vulk.dev/terms-conditions
servers:
- url: https://app.vulk.dev/api/v1
  description: Production
security:
- ApiKeyAuth: []
tags:
- name: Projects
  description: Start, continue, list, read and delete projects; read their delivered files.
paths:
  /projects:
    get:
      tags:
      - Projects
      operationId: listProjects
      summary: List projects
      description: Scope `projects.read`. Newest first.
      parameters:
      - name: limit
        in: query
        description: How many to return, 1 to 100.
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
      - name: offset
        in: query
        description: How many to skip, 0 to 1000.
        schema:
          type: integer
          minimum: 0
          maximum: 1000
          default: 0
      responses:
        '200':
          description: The account's projects.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectList'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
      - Projects
      operationId: createProject
      summary: Create a project from a brief
      description: Scope `projects.write`. Starts a build and answers an admission receipt; the build runs after the answer and spends the credits of your plan, as the same brief typed into the editor would. Follow it with GET /operations/{requestId}.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Instruction'
            example:
              prompt: 'A landing page for a bakery in Porto: hero with the shop, the week''s bread, opening hours, a map and a contact form.'
              model: auto
              requestId: 7a1c0b7e-6a0f-4b0e-9a63-0f5c1b2d3e4f
      responses:
        '202':
          description: 'Admission receipt. The build runs after this answer; follow it with GET /operations/{requestId}. `accepted: false` with `operationStatus: "unconfirmed"` means VULK had not recorded the operation within the wait: read the operation, then retry with exactly the same requestId and body.'
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdmissionReceipt'
              example:
                requestId: 7a1c0b7e-6a0f-4b0e-9a63-0f5c1b2d3e4f
                accepted: true
                operationStatus: running
                projectId: cmgp3x8d7000a0wz4h2k1qv9r
                editorUrl: https://app.vulk.dev/chat/cmgp3x8d7000a0wz4h2k1qv9r
                nextAction: Read GET /operations/7a1c0b7e-… until operationStatus is done, waiting or failed.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /projects/{id}:
    parameters:
    - name: id
      in: path
      required: true
      description: A project id as returned by GET /projects. A project on another account answers 404, never 403.
      schema:
        type: string
    get:
      tags:
      - Projects
      operationId: getProject
      summary: Read a project
      description: Scope `projects.read`.
      responses:
        '200':
          description: The project, its versions and its addresses.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    delete:
      tags:
      - Projects
      operationId: deleteProject
      summary: Delete a project
      description: Scope `projects.write`. Deletes the project; the action is recorded against the key.
      responses:
        '200':
          description: Deleted.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deleted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /projects/{id}/files:
    parameters:
    - name: id
      in: path
      required: true
      description: A project id as returned by GET /projects. A project on another account answers 404, never 403.
      schema:
        type: string
    get:
      tags:
      - Projects
      operationId: getProjectFiles
      summary: Read the delivered files
      description: Scope `export.read`, on a plan that includes export. Without `path`, lists the files of the project's delivered version; with `path`, returns one file.
      parameters:
      - name: path
        in: query
        description: A path from the file list, e.g. `src/App.tsx`.
        schema:
          type: string
      responses:
        '200':
          description: The file list, or one file when `path` is given.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                oneOf:
                - $ref: '#/components/schemas/FileList'
                - $ref: '#/components/schemas/FileContent'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /projects/{id}/messages:
    parameters:
    - name: id
      in: path
      required: true
      description: A project id as returned by GET /projects. A project on another account answers 404, never 403.
      schema:
        type: string
    post:
      tags:
      - Projects
      operationId: sendInstruction
      summary: Send the next instruction
      description: 'Scope `projects.write`. The same body and the same receipt as POST /projects, applied to an existing project: `prompt` is the next instruction — or the answer to a question the operation is waiting for.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Instruction'
            example:
              prompt: 'A landing page for a bakery in Porto: hero with the shop, the week''s bread, opening hours, a map and a contact form.'
              model: auto
              requestId: 7a1c0b7e-6a0f-4b0e-9a63-0f5c1b2d3e4f
      responses:
        '202':
          description: 'Admission receipt. The build runs after this answer; follow it with GET /operations/{requestId}. `accepted: false` with `operationStatus: "unconfirmed"` means VULK had not recorded the operation within the wait: read the operation, then retry with exactly the same requestId and body.'
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdmissionReceipt'
              example:
                requestId: 7a1c0b7e-6a0f-4b0e-9a63-0f5c1b2d3e4f
                accepted: true
                operationStatus: running
                projectId: cmgp3x8d7000a0wz4h2k1qv9r
                editorUrl: https://app.vulk.dev/chat/cmgp3x8d7000a0wz4h2k1qv9r
                nextAction: Read GET /operations/7a1c0b7e-… until operationStatus is done, waiting or failed.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  schemas:
    Deleted:
      type: object
      properties:
        success:
          type: boolean
        id:
          type: string
        deletedAt:
          type: string
          format: date-time
      required:
      - success
      - id
      - deletedAt
    Instruction:
      type: object
      properties:
        prompt:
          type: string
          minLength: 1
          maxLength: 8000
          description: The brief, or the next instruction, in any language. It is handed to VULK unchanged.
        model:
          type: string
          description: A model id from GET /models, or `auto`. Omit it to let VULK choose. A model your plan does not include answers 402.
          example: auto
        requestId:
          type: string
          format: uuid
          description: 'A UUID v4 you make. Send the same requestId to retry a submit whose answer was lost: the API returns the operation it already created instead of starting a second one. Omitted, VULK makes one.'
      required:
      - prompt
    AdmissionReceipt:
      type: object
      properties:
        requestId:
          type: string
          format: uuid
        accepted:
          type: boolean
          description: True when VULK recorded the operation.
        operationStatus:
          type:
          - string
          - 'null'
          enum:
          - running
          - waiting
          - done
          - failed
          - orphan
          - unconfirmed
          - null
          description: '`unconfirmed` when `accepted` is false.'
        projectId:
          type: string
        editorUrl:
          type: string
          format: uri
        nextAction:
          type: string
          description: What to do next, in a sentence written for the caller.
      required:
      - requestId
      - accepted
      - operationStatus
      - nextAction
    FileContent:
      type: object
      properties:
        projectId:
          type: string
        path:
          type: string
        bytes:
          type: integer
        content:
          type: string
          description: The file as UTF-8 text; cut after 200,000 characters, with `truncated` true.
        truncated:
          type: boolean
      required:
      - projectId
      - path
      - bytes
      - content
      - truncated
    Error:
      type: object
      description: Every error has the same three fields; `code` is the value to branch on. A 429 also carries `retryAfter`.
      properties:
        error:
          type: string
          description: A sentence for the developer reading a log.
        code:
          type: string
          enum:
          - VALIDATION_ERROR
          - UNAUTHORIZED
          - PAYMENT_REQUIRED
          - FORBIDDEN
          - NOT_FOUND
          - METHOD_NOT_ALLOWED
          - CONFLICT
          - PAYLOAD_TOO_LARGE
          - RATE_LIMITED
          - SERVER_ERROR
          - SERVICE_UNAVAILABLE
        status:
          type: integer
        retryAfter:
          type: integer
          description: Seconds to wait; only on 429.
      required:
      - error
      - code
      - status
      example:
        error: 'Missing API key. Send it as "Authorization: Bearer vk_sk_…". Keys are created in Settings › API keys.'
        code: UNAUTHORIZED
        status: 401
    ProjectSummary:
      type: object
      properties:
        id:
          type: string
          example: cmgp3x8d7000a0wz4h2k1qv9r
        title:
          type: string
        kind:
          type: string
          enum:
          - app
          - image
          - video
          - mesh
          description: '`app` for a website or app; `image`, `video` or `mesh` for a standalone design asset.'
        createdAt:
          type: string
          format: date-time
        url:
          type: string
          format: uri
          description: The project's page in the editor.
      required:
      - id
      - title
      - kind
      - createdAt
      - url
    ProjectList:
      type: object
      properties:
        projects:
          type: array
          items:
            $ref: '#/components/schemas/ProjectSummary'
        count:
          type: integer
        pagination:
          type: object
          properties:
            limit:
              type: integer
            offset:
              type: integer
            count:
              type: integer
            hasMore:
              type: boolean
          required:
          - limit
          - offset
          - count
          - hasMore
      required:
      - projects
      - count
      - pagination
    Project:
      type: object
      description: '`versions` counts the builds; `files` and `bytes` describe the latest one. `siteName` and `liveUrl` are set once the project is published.'
      properties:
        id:
          type: string
        title:
          type: string
        folder:
          type: string
        versions:
          type: integer
        versionsWithFiles:
          type: integer
        files:
          type: integer
        bytes:
          type: integer
        lastVersionStatus:
          type: string
          description: A word such as `delivered` or `failed`.
        lastVersionError:
          type:
          - string
          - 'null'
          description: An error code when the last version failed.
        siteName:
          type:
          - string
          - 'null'
        previewUrl:
          type:
          - string
          - 'null'
          format: uri
        liveUrl:
          type:
          - string
          - 'null'
          format: uri
        url:
          type: string
          format: uri
          description: The project's page in the editor.
      required:
      - id
      - title
      - folder
      - versions
      - versionsWithFiles
      - files
      - bytes
      - lastVersionStatus
      - url
    FileList:
      type: object
      properties:
        projectId:
          type: string
        version:
          type: integer
          description: The delivered version the files belong to.
        files:
          type: array
          items:
            type: object
            properties:
              path:
                type: string
                example: src/App.tsx
              bytes:
                type: integer
            required:
            - path
            - bytes
        totalFiles:
          type: integer
        totalBytes:
          type: integer
        truncated:
          type: boolean
      required:
      - projectId
      - version
      - files
      - totalFiles
      - totalBytes
      - truncated
  responses:
    RateLimited:
      description: Over 60 requests in a minute; wait `retryAfter` seconds (code RATE_LIMITED).
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: No such endpoint, project, operation or file on this account (code NOT_FOUND).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The key lacks the scope the endpoint needs (code FORBIDDEN).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: A parameter or body field is wrong; the sentence names it (code VALIDATION_ERROR).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: The plan or credit balance does not cover the request (code PAYMENT_REQUIRED).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: No key, a malformed key, or a revoked or expired one (code UNAUTHORIZED).
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WWW-Authenticate'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServiceUnavailable:
      description: Generation or a dependency is temporarily unavailable; nothing was submitted (code SERVICE_UNAVAILABLE).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PayloadTooLarge:
      description: The body is over 64 KB (code PAYLOAD_TOO_LARGE).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: The project has no delivered version with files yet (code CONFLICT).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  headers:
    Retry-After:
      description: Seconds to wait before the next request.
      schema:
        type: integer
        example: 23
    X-RateLimit-Remaining:
      description: Requests left in the current minute.
      schema:
        type: integer
        example: 42
    X-RateLimit-Reset:
      description: When the window resets, in Unix seconds.
      schema:
        type: integer
        example: 1760360400
    X-RateLimit-Limit:
      description: Requests the key may make per minute.
      schema:
        type: integer
        example: 60
    WWW-Authenticate:
      description: Always `Bearer realm="VULK API"`.
      schema:
        type: string
        example: Bearer realm="VULK API"
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: vk_sk_*
      description: 'Create a key in Settings › API keys (https://app.vulk.dev/settings?tab=ApiKeys) and send it as `Authorization: Bearer vk_sk_<key>`. Each endpoint names the scope the key needs; a key without it is answered 403.'
externalDocs:
  description: API reference — every endpoint, scopes, errors and rate limits
  url: https://support.vulk.dev/docs/api