Kernel Extensions API
Create, list, retrieve, and delete browser extensions.
Create, list, retrieve, and delete browser extensions.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/kernel-extensions-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
openapi: 3.1.0
info:
title: Kernel API Keys Extensions API
description: Developer tools and cloud infrastructure for AI agents to use web browsers
version: 0.1.0
servers:
- url: https://api.onkernel.com
description: API Server
security:
- bearerAuth: []
tags:
- name: Extensions
description: Create, list, retrieve, and delete browser extensions.
paths:
/extensions:
post:
operationId: postExtensions
tags:
- Extensions
summary: Upload a browser extension
description: Upload a zip file containing an unpacked browser extension. Optionally provide a unique name for later reference.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: ZIP file containing the browser extension.
example: '@path/to/extension.zip'
name:
type: string
description: Optional unique name within the project to reference this extension.
pattern: ^[A-Za-z0-9._-]{1,255}$
required:
- file
responses:
'201':
description: Extension uploaded successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Extension'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import fs from 'fs';\nimport Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.extensions.upload({ file: fs.createReadStream('path/to/file') });\n\nconsole.log(response.id);"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\nresponse = client.extensions.upload(\n file=b\"Example data\",\n)\nprint(response.id)"
- lang: Go
source: "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tresponse, err := client.Extensions.Upload(context.TODO(), kernel.ExtensionUploadParams{\n\t\tFile: io.Reader(bytes.NewBuffer([]byte(\"Example data\"))),\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response.ID)\n}\n"
get:
operationId: getExtensions
tags:
- Extensions
summary: List browser extensions
description: List extensions in the resolved project.
security:
- bearerAuth: []
parameters:
- name: limit
in: query
required: false
description: Limit the number of extensions to return.
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: offset
in: query
required: false
description: Offset the number of extensions to return.
schema:
type: integer
minimum: 0
default: 0
- name: query
in: query
required: false
description: Case-insensitive substring match against extension name. IDs match by exact value.
schema:
type: string
- name: name
in: query
required: false
description: Exact-match filter on extension name using the database collation. In production, matching is case- and accent-insensitive. During the default-project migration, unscoped requests prefer a concrete default-project extension over a legacy unscoped extension with the same name.
schema:
type: string
responses:
'200':
description: List of extensions
headers:
X-Limit:
description: Limit the number of extensions to return.
schema:
type: integer
minimum: 1
maximum: 100
default: 20
X-Offset:
description: The offset of extensions to return.
schema:
type: integer
minimum: 0
default: 0
X-Next-Offset:
description: The offset where the next page starts. 0 when there are no more results.
schema:
type: integer
nullable: true
X-Has-More:
description: Whether there are more extensions to fetch.
schema:
type: boolean
default: false
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Extension'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\n// Automatically fetches more pages as needed.\nfor await (const extensionListResponse of client.extensions.list()) {\n console.log(extensionListResponse.id);\n}"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\npage = client.extensions.list()\npage = page.items[0]\nprint(page.id)"
- lang: Go
source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tpage, err := client.Extensions.List(context.TODO(), kernel.ExtensionListParams{})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
/extensions/{id_or_name}:
get:
operationId: getExtensionByIdOrName
tags:
- Extensions
summary: Download extension archive
description: Download the extension as a ZIP archive by ID or name.
security:
- bearerAuth: []
parameters:
- name: id_or_name
in: path
required: true
schema:
type: string
description: Extension ID or name
responses:
'200':
description: Extension ZIP archive
content:
application/octet-stream:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.extensions.download('id_or_name');\n\nconsole.log(response);\n\nconst content = await response.blob();\nconsole.log(content);"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\nresponse = client.extensions.download(\n \"id_or_name\",\n)\nprint(response)\ncontent = response.read()\nprint(content)"
- lang: Go
source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tresponse, err := client.Extensions.Download(context.TODO(), \"id_or_name\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response)\n}\n"
delete:
operationId: deleteExtensionByIdOrName
tags:
- Extensions
summary: Delete extension by ID or name
description: Delete an extension by its ID or by its name.
security:
- bearerAuth: []
parameters:
- name: id_or_name
in: path
required: true
schema:
type: string
description: Extension ID or extension name
responses:
'204':
description: Extension deleted successfully
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\nawait client.extensions.delete('id_or_name');"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\nclient.extensions.delete(\n \"id_or_name\",\n)"
- lang: Go
source: "package main\n\nimport (\n\t\"context\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\terr := client.Extensions.Delete(context.TODO(), \"id_or_name\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
/extensions/{id_or_name}/metadata:
get:
operationId: getExtensionByIdOrNameMetadata
tags:
- Extensions
summary: Get extension metadata
description: Get an extension's metadata (name, size, timestamps) by ID or name, without downloading the archive.
security:
- bearerAuth: []
parameters:
- name: id_or_name
in: path
required: true
schema:
type: string
description: Extension ID or name
responses:
'200':
description: Extension metadata
content:
application/json:
schema:
$ref: '#/components/schemas/Extension'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\nconst extension = await client.extensions.get('id_or_name');\n\nconsole.log(extension.id);"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\nextension = client.extensions.get(\n \"id_or_name\",\n)\nprint(extension.id)"
- lang: Go
source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\textension, err := client.Extensions.Get(context.TODO(), \"id_or_name\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", extension.ID)\n}\n"
/extensions/from_chrome_store:
get:
operationId: getExtensionsFromChromeStore
tags:
- Extensions
summary: Download unpacked extension from Chrome Web Store
description: Returns a ZIP archive containing the unpacked extension fetched from the Chrome Web Store.
security:
- bearerAuth: []
parameters:
- name: url
in: query
required: true
schema:
type: string
description: Chrome Web Store URL for the extension.
- name: os
in: query
required: false
schema:
type: string
enum:
- win
- mac
- linux
default: linux
description: Target operating system for the extension package. Defaults to linux.
responses:
'200':
description: Extension ZIP archive
content:
application/octet-stream:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalError'
x-codeSamples:
- lang: JavaScript
source: "import Kernel from '@onkernel/sdk';\n\nconst client = new Kernel({\n apiKey: process.env['KERNEL_API_KEY'], // This is the default and can be omitted\n});\n\nconst response = await client.extensions.downloadFromChromeStore({ url: 'url' });\n\nconsole.log(response);\n\nconst content = await response.blob();\nconsole.log(content);"
- lang: Python
source: "import os\nfrom kernel import Kernel\n\nclient = Kernel(\n api_key=os.environ.get(\"KERNEL_API_KEY\"), # This is the default and can be omitted\n)\nresponse = client.extensions.download_from_chrome_store(\n url=\"url\",\n)\nprint(response)\ncontent = response.read()\nprint(content)"
- lang: Go
source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/kernel/kernel-go-sdk\"\n\t\"github.com/kernel/kernel-go-sdk/option\"\n)\n\nfunc main() {\n\tclient := kernel.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tresponse, err := client.Extensions.DownloadFromChromeStore(context.TODO(), kernel.ExtensionDownloadFromChromeStoreParams{\n\t\tURL: \"url\",\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", response)\n}\n"
components:
responses:
Conflict:
description: Conflict – resource already exists
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
InternalError:
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Unauthorized – missing or invalid authorization token
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Bad Request – invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Forbidden – insufficient permissions or plan
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
Error:
type: object
required:
- code
- message
properties:
code:
type: string
description: Application-specific error code (machine-readable)
example: bad_request
message:
type: string
description: Human-readable error description for debugging
example: 'Missing required field: app_name'
details:
type: array
description: Additional error details (for multiple errors)
items:
$ref: '#/components/schemas/ErrorDetail'
inner_error:
$ref: '#/components/schemas/ErrorDetail'
Extension:
type: object
description: A browser extension uploaded to Kernel.
properties:
id:
type: string
description: Unique identifier for the extension
name:
type: string
nullable: true
description: Optional, easier-to-reference name for the extension. Must be unique within the project.
created_at:
type: string
format: date-time
description: Timestamp when the extension was created
size_bytes:
type: integer
description: Size of the extension archive in bytes
checksum:
type: string
nullable: true
minLength: 64
maxLength: 64
description: SHA-256 checksum, encoded as lowercase hexadecimal, of the exact uploaded extension archive bytes. This is not a normalized checksum of the extension contents; archive metadata, file ordering, and compression can change the checksum for otherwise identical contents. Omitted for legacy rows and server-repackaged Chrome Web Store extensions.
last_used_at:
type: string
format: date-time
nullable: true
description: Timestamp when the extension was last used
required:
- id
- created_at
- size_bytes
ErrorDetail:
type: object
properties:
code:
type: string
description: Lower-level error code providing more specific detail
example: invalid_input
message:
type: string
description: Further detail about the error
example: Provided version string is not semver compliant
securitySchemes:
bearerAuth:
type: http
scheme: bearer