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

rpcflow-api

v1.0.0

Published

Clean Node.js client for Google Flow batchexecute — images, video (text/frames/ingredients), session sync, poll.

Downloads

152

Readme

rpcflow-api

A compact Node.js client for Google Flow (batchexecute): session sync, model/project listing, image generation, video generation (text / frames / ingredients), and polling until media URLs are ready.

Requires Node ≥ 18 (native fetch). Zero runtime dependencies.

Install

npm install rpcflow-api

Requirements

| Item | Notes | |------|--------| | Node.js | >= 18 | | Cookie | Logged-in cookie string from flow.google.com | | Activation key (apiKey) | Required on every new RpcFlow(...) — contact the author if you need a key | | Image reCAPTCHA | Token with ?type=image for createImage / some uploads | | Video reCAPTCHA | Separate token (without ?type=image) for createVideo |

The library does not read secrets from environment variables. Pass cookie, apiKey, tokens, and optional userAgent yourself.

Quick start

const RpcFlow = require('rpcflow-api');
// or: const { RpcFlow } = require('rpcflow-api');

const cookie = process.env.FLOW_COOKIE; // cookie from flow.google.com
const apiKey = process.env.RPCFLOW_API_KEY; // your activation key (do not hardcode in public repos)
const userAgent =
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36';

const flow = new RpcFlow(cookie, { apiKey, userAgent });
// or: new RpcFlow(cookie, apiKey);
// omit userAgent → built-in default is used

// 1) Sync account → at / bl / sid / projectId (+ Bearer when available)
const session = await flow.sync_session();
console.log(session);

// 2) Load session into the instance (you can persist JSON and init later)
flow.init(session);

// 3) Models & projects (pretty output)
console.log(String(flow.models())); // image + video model table
console.log((await flow.projects()).text); // project list

// 4) Create image (needs reCAPTCHA token with type=image)
const imageJob = await flow.createImage({
  prompt: 'soft morning light over rice fields',
  model: 'NARWHAL', // NARWHAL | GEM_PIX_2 | IMAGEN_3_5
  aspectRatio: '16:9',
  sessionToken: process.env.IMAGE_TOKEN,
  // referenceMediaIds: ['<mediaId>'],
});
console.log(imageJob.images);

// 5) Create text video → poll / wait
const videoJob = await flow.createVideo({
  mode: 'text',
  prompt: 'gentle nature drone shot',
  model: 'abra_t2v_8s',
  aspectRatio: '16:9',
  sessionToken: process.env.VIDEO_TOKEN, // do NOT reuse the image token
});

const done = await flow.wait(videoJob, { intervalMs: 5000, timeoutMs: 180000 });
console.log(done.videos);

Activation key

apiKey is required. Without a valid key the constructor throws and no RPC calls run.

new RpcFlow(cookie, { apiKey: process.env.RPCFLOW_API_KEY });
new RpcFlow(cookie, process.env.RPCFLOW_API_KEY);

| Error code | When | |------------|------| | API_KEY_REQUIRED | Missing / empty key | | API_KEY_INVALID | Wrong key |

Keys are distributed privately — they are not documented in this README. Contact the author (see Donate) if you need access.

API

new RpcFlow(cookie, apiKey) / new RpcFlow(cookie, options)

new RpcFlow(cookie, apiKey);
new RpcFlow(cookie, { apiKey });
new RpcFlow(cookie, { apiKey, userAgent: 'Mozilla/5.0 …', hl: 'en' });
new RpcFlow(cookie, { apiKey, session: savedSession });

| Arg | Required | Description | |-----|----------|-------------| | cookie | yes | Cookie string from flow.google.com | | apiKey | yes | Activation key | | userAgent | no | Custom UA; default built into the library (never read from env) | | session | no | Preloaded session → skip a later init | | hl | no | batchexecute locale (default en) |

Instance getters: flow.session, flow.userAgent.

await sync_session(opts?) / syncSession()

Scrapes at, bl, sid, projectId from Flow HTML; optionally fetches Labs OAuth ya29….

| Option | Default | Description | |--------|---------|-------------| | fetchBearer | true | Try to obtain Labs Bearer | | flowUrl | Flow home | Override start URL | | userAgent | instance UA | Override for this call only |

Returns a session object — it is not applied until you call init(session).

init(session)

Apply a session (from sync_session or a saved JSON file). Requires session.at.

models(opts?)

Wire-safe model catalog (no network).

flow.models(); // pretty string (safe to console.log)
flow.models({ pretty: false }); // { image, video, all }
flow.models({ kind: 'image' | 'video' | 'all' });

await modelsLive()

Hits live RPC HTrJv and returns raw results plus the known catalog.

await projects(opts?)

const { projects, text } = await flow.projects();
flow.init({ ...flow.session, projectId: projects[0].id });

If the instance has no projectId yet, the first project id is stored automatically.

await credits()

Raw credits RPC (nzlxg).

await uploadImage({ imageBytes, mimeType?, fileName?, sessionToken?, projectId?, variant? })

Upload a reference / frame image.

| Field | Notes | |-------|--------| | imageBytes | Base64 or data: URL | | mimeType | Default image/png | | sessionToken | Image captcha when required | | projectId | Falls back to session |

Returns { ok, mediaId, projectId, variant, … }.

await createImage({ prompt, sessionToken, model?, aspectRatio?, seed?, referenceMediaIds?, projectId? })

| Field | Default / notes | |-------|-----------------| | prompt | required | | sessionToken | required (image reCAPTCHA) | | model | NARWHAL (GEM_PIX_2, IMAGEN_3_5) | | aspectRatio | 16:9 (1:1, 9:16, 4:3, 3:4, …) | | seed | random int32-safe | | referenceMediaIds | up to 10 media ids from uploadImage |

Returns { kind:'image', done, images:[{ mediaId, url, … }], request, … }. URLs are usually ready immediately — poll / wait are optional.

await createVideo({ prompt, sessionToken, model, mode?, … })

| Field | Notes | |-------|--------| | prompt | required | | sessionToken | required (video reCAPTCHA) | | model | required — see flow.models({ kind:'video' }) | | mode | text (default) | frames | ingredients | | aspectRatio | 16:9 | 9:16 | 1:1 | | startMediaId / endMediaId | frames mode | | referenceMediaIds | ingredients mode (1–7) | | projectId | falls back to session |

| mode | Extra fields | |--------|----------------| | text | — | | frames | startMediaId, optional endMediaId | | ingredients | referenceMediaIds (1–7) |

Returns { kind:'video', operations, record, poll, videos, … }. Call poll / wait until videos[].url appears.

await poll(job) / await pollVideo(job) / await pollImage(job)

One status tick. poll routes by job.kind.

Video tick chain: jwpduf → as29s → uurnC → Zzl0ze.

await wait(job, { intervalMs?, timeoutMs? })

| Option | Default | |--------|---------| | intervalMs | 5000 | | timeoutMs | 180000 |

Polls until a video URL appears, the job fails, or the timeout is hit. For image jobs, returns immediately if already done.

snake_case aliases

sync_session, create_image, create_video, upload_image, poll_video, poll_image, …

Escape hatch

await flow.rpc('HTrJv', []);

Persist session

const fs = require('fs');
const session = await flow.sync_session();
fs.writeFileSync('session.json', JSON.stringify(session, null, 2));

// later
const saved = JSON.parse(fs.readFileSync('session.json', 'utf8'));
const flow2 = new RpcFlow(saved.cookie, { apiKey: process.env.RPCFLOW_API_KEY });
flow2.init(saved);

Frames / Ingredients examples

const up = await flow.uploadImage({
  imageBytes: fs.readFileSync('frame.jpg').toString('base64'),
  mimeType: 'image/jpeg',
  sessionToken: process.env.IMAGE_TOKEN,
});

// single start frame
await flow.createVideo({
  mode: 'frames',
  prompt: 'slow push in',
  model: 'abra_t2v_8s',
  startMediaId: up.mediaId,
  sessionToken: process.env.VIDEO_TOKEN,
});

// start + end
await flow.createVideo({
  mode: 'frames',
  prompt: 'morph start to end',
  model: 'abra_t2v_8s',
  startMediaId: startId,
  endMediaId: endId,
  sessionToken: process.env.VIDEO_TOKEN,
});

// ingredients
await flow.createVideo({
  mode: 'ingredients',
  prompt: 'match these references',
  model: 'abra_t2v_8s',
  referenceMediaIds: [id1, id2],
  sessionToken: process.env.VIDEO_TOKEN,
});

Common errors

| Code | Meaning | |------|---------| | API_KEY_REQUIRED / API_KEY_INVALID | Missing or wrong activation key | | SESSION_SYNC_FAILED / AT_MISSING | Cookie expired / not logged in | | NOT_INITIALIZED | Call sync_session + init first | | PROJECT_REQUIRED | No projectId on session or request | | IMAGE_TOKEN_REQUIRED / VIDEO_TOKEN_REQUIRED | Missing captcha token | | PUBLIC_ERROR_UNUSUAL_ACTIVITY | Anti-bot — mint a new token, slow down requests | | PUBLIC_ERROR_USER_QUOTA_REACHED | Quota exhausted | | INVALID_ARGUMENT | Bad payload / model / token | | MODEL_NOT_SUPPORTED | Model not in the catalog | | UPLOAD_FAILED | All upload payload variants rejected | | RECORD_REQUIRED | pollVideo needs CAE record from createVideo |

Package layout

rpcflow-api/
  package.json
  README.md
  src/
    index.js          # exports
    RpcFlow.js        # class API
    license.js        # activation gate
    models.js         # catalog + pretty print
    errors.js
    utils.js
    auth/             # cookie → at/bl/sid + Bearer
    client/           # batchexecute + parse
    image/            # payloads ogiZ0b / maseQ
    video/            # payloads YhhmEf / eb1hJf / nprQif / MZZa6b + poll

Wire shapes are measured from the Flow frontend; update payloads under src/image / src/video when Google changes RPCs.

Publish to npm

cd rpcflow-api
npm login
npm publish --access public

License

MIT

Donate

If this library helps you, consider supporting via PayPal:

PayPal: [email protected]