npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@apicity/kie

v0.1.0

Published

Kie provider for video and image generation (Kling 3.0, Grok Imagine, Nano Banana Pro).

Readme

@apicity/kie

npm zero dependencies TypeScript

Kie provider for video and image generation (Kling 3.0, Grok Imagine, Nano Banana Pro).

Installation

npm install @apicity/kie
# or
pnpm add @apicity/kie

Quick Start

import { kie as createKie } from "@apicity/kie";

const kie = createKie({ apiKey: process.env.KIE_API_KEY! });

Real-world example: Kling 3.0 video with named element references

Kling 3.0/video has a feature most upstream video models lack — named element references. You upload reference images for each subject, give the subject a name and description, and refer back to it from the prompt with [name] placeholders. The result preserves identity across frames without prompt-engineering gymnastics.

The flow below is taken verbatim from tests/integration/kie-kling-30-reference-bakeoff.test.ts and replays against tests/recordings/kie_2079838932/kling-30-reference-bakeoff_875607413/recording.har, so the response shapes match what Kie actually returns.

import { readFileSync } from "node:fs";
import { kie as createKie } from "@apicity/kie";

const kie = createKie({ apiKey: process.env.KIE_API_KEY! });

// 1. Upload each reference image. Kie returns a CDN-hosted
//    `downloadUrl` you'll thread through the job request — the bytes
//    themselves never live in the createTask payload.
async function upload(filename: string, mimeType: string): Promise<string> {
  const blob = new Blob([readFileSync(filename)], { type: mimeType });
  const res = await kie.post.api.fileStreamUpload({
    file: blob,
    filename,
    uploadPath: "images/test-uploads",
  });
  if (!res.data?.downloadUrl) throw new Error("upload failed");
  return res.data.downloadUrl;
}

// Two angles of the cat satisfy Kling's "2-4 images per element"
// minimum. The man only has one fixture, so we pass it twice.
const cat1  = await upload("cat1.jpg",  "image/jpeg");
const cat2  = await upload("cat2.jpg",  "image/jpeg");
const man   = await upload("man.jpg",   "image/jpeg");
const beach = await upload("beach.png", "image/png");

// 2. Submit the job. Each `kling_elements` entry binds a `name` to a
//    set of reference images; the prompt then refers to that subject
//    via `[name]`. `image_urls` carries general/setting references —
//    the beach plate here — that aren't tied to a named subject.
const task = await kie.post.api.v1.jobs.createTask({
  model: "kling-3.0/video",
  input: {
    prompt:
      "On a sandy beach with the ocean behind, [blue_suit_man] sits " +
      "cross-legged on the sand. [white_cat] climbs onto his lap, " +
      "purrs, and lifts a paw to bat playfully at his blue tie. " +
      "[blue_suit_man] smiles and waves at the camera with his free hand.",
    image_urls: [beach],
    kling_elements: [
      {
        name: "white_cat",
        description: "A white cat with mismatched yellow and blue eyes",
        element_input_urls: [cat1, cat2],
      },
      {
        name: "blue_suit_man",
        description: "A man wearing a blue suit and a blue tie",
        element_input_urls: [man, man],
      },
    ],
    sound: false,
    duration: "5",
    aspect_ratio: "16:9",
    mode: "std",
    multi_shots: false,
  },
});

if (!task.data?.taskId) throw new Error("createTask returned no taskId");
const taskId = task.data.taskId;
// → "56074f12319c68b246e5a03e05608f31"

// 3. Poll recordInfo until the job leaves the `generating` state.
//    Kie's terminal states are `success` and `fail` — anything else
//    (`waiting`, `queuing`, `generating`) means keep waiting.
let state: string = "waiting";
let resultJson: string | undefined;
for (let i = 0; i < 200; i++) {
  const info = await kie.get.api.v1.jobs.recordInfo(taskId);
  state = info.data?.state ?? "waiting";
  if (state === "success" || state === "fail") {
    resultJson = info.data?.resultJson;
    break;
  }
  await new Promise((r) => setTimeout(r, 10_000));
}
if (state !== "success" || !resultJson) throw new Error(`job ${state}`);

// 4. `resultJson` is a JSON-encoded string — parse it to get the
//    delivered media URLs. The shape is consistent across every kie
//    media model (single `resultUrls: string[]`).
const result = JSON.parse(resultJson) as { resultUrls: string[] };
console.log(result.resultUrls[0]);
// → "https://tempfile.aiquickdraw.com/k/56074f12319c68b246e5a03e05608f31_1_1777540551_7969.mp4"

Notes

  • kling_elements accepts at most 3 named subjects, and each element_input_urls array must hold 2–4 images. If you only have one reference per subject, repeat it (as the example does for man) to satisfy the minimum.
  • The poll loop watches for the terminal states success and fail — anything else (waiting, queuing, generating) means keep going. There's no check_after_secs hint as on some other providers, so a 10s cadence is conservative; in the recorded fixture the job completed after ~127s of generating (14 polls).
  • kie.post.api.v1.jobs.createTask is the unified entry point for every media model in this provider — Kling, Wan, Seedance, Grok Imagine, GPT-Image-2, Qwen2, and others. Swap the model and input block; the rest of the flow (upload → createTask → poll → parse resultJson) is identical.
  • For convenience, submitMediaJob(provider, request) and uploadFile(provider, blob, filename, uploadPath) re-export the same calls but throw KieError directly when the upstream envelope is missing the expected fields.
  • Errors surface as KieError with status, body, and an upstream code attached, so try { ... } catch (e) { if (e instanceof KieError) ... } gives you the upstream error directly.

API Reference

26 endpoints across 15 groups. Each method mirrors an upstream URL path.

chat

GET https://api.kie.ai/api/v1/chat/credit

Upstream docs ↗

const res = await kie.get.api.v1.chat.credit({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

claude

POST https://api.kie.ai/claude/v1/messages

Upstream docs ↗

const res = await kie.claude.post.v1.messages({ /* ... */ });

Source: packages/provider/kie/src/claude.ts

common

POST https://api.kie.ai/api/v1/common/download-url

Upstream docs ↗

const res = await kie.post.api.v1.common.downloadUrl({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

fileBase64Upload

POST https://api.kie.ai/api/file-base64-upload

Upstream docs ↗

const res = await kie.post.api.fileBase64Upload({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

fileStreamUpload

POST https://api.kie.ai/api/file-stream-upload

Upstream docs ↗

const res = await kie.post.api.fileStreamUpload({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

fileUrlUpload

POST https://api.kie.ai/api/file-url-upload

Upstream docs ↗

const res = await kie.post.api.fileUrlUpload({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

generate

GET https://api.kie.ai/api/v1/generate/record-info?taskId={taskId}

const res = await kie.suno.get.api.v1.generate.recordInfo({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/add-instrumental

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate.addInstrumental({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/add-vocals

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate.addVocals({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/extend

const res = await kie.suno.post.api.v1.generate.extend({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/mashup

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate.mashup({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/replace-section

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate.replaceSection({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/sounds

Upstream docs ↗

const res = await kie.suno.post.api.v1.generate.sounds({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/upload-cover

const res = await kie.suno.post.api.v1.generate.uploadCover({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

POST https://api.kie.ai/api/v1/generate/upload-extend

const res = await kie.suno.post.api.v1.generate.uploadExtend({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

jobs

GET https://api.kie.ai/api/v1/jobs/recordInfo?taskId={taskId}

Upstream docs ↗

const res = await kie.get.api.v1.jobs.recordInfo({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

POST https://api.kie.ai/api/v1/jobs/createTask

Upstream docs ↗

const res = await kie.post.api.v1.jobs.createTask({ /* ... */ });

Source: packages/provider/kie/src/kie.ts

lyrics

POST https://api.kie.ai/api/v1/lyrics

const res = await kie.suno.post.api.v1.lyrics({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

midi

POST https://api.kie.ai/api/v1/midi/generate

const res = await kie.suno.post.api.v1.midi.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

mp4

POST https://api.kie.ai/api/v1/mp4/generate

const res = await kie.suno.post.api.v1.mp4.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

style

POST https://api.kie.ai/api/v1/style/generate

const res = await kie.suno.post.api.v1.style.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

veo

POST https://api.kie.ai/api/v1/veo/extend

Upstream docs ↗

const res = await kie.veo.post.api.v1.veo.extend({ /* ... */ });

Source: packages/provider/kie/src/veo.ts

POST https://api.kie.ai/api/v1/veo/generate

Upstream docs ↗

const res = await kie.veo.post.api.v1.veo.generate({ /* ... */ });

Source: packages/provider/kie/src/veo.ts

vocalRemoval

POST https://api.kie.ai/api/v1/vocal-removal/generate

const res = await kie.suno.post.api.v1.vocalRemoval.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

wav

POST https://api.kie.ai/api/v1/wav/generate

const res = await kie.suno.post.api.v1.wav.generate({ /* ... */ });

Source: packages/provider/kie/src/suno.ts

Middleware

import { kie as createKie, withRetry } from "@apicity/kie";

const kie = createKie({ apiKey: process.env.KIE_API_KEY! });
const models = withRetry(kie.get.v1.models, { retries: 3 });

Part of the apicity monorepo.

License

MIT — see LICENSE.