Announcing CIMD support for MCP Client registration
Learn more

Give Your Apify Actors Real User Accounts with Scalekit

TL;DR

  • An Apify Actor is stateless; every run starts cold with no current user, so per-customer OAuth needs an external token store.
  • Use Actor.getEnv().userId as the Scalekit identifier so users never type an email or ID.
  • Decide per connector: shared (one hardcoded identifier) or per-user (derived from userId).
  • Serve the consent step as a page on Apify's live-view port instead of a raw link.
  • Scalekit stores and refreshes the OAuth token per (connection, identifier), so each user authorizes once.

Every Apify Actor run starts cold — stateless, no memory of who is running it or what accounts it should use. The moment you want the Actor to write scraped results into a customer's own Notion workspace, pull analytics from their YouTube channel, or send messages from their Gmail, you hit the classic production problem: how do you give the Actor the right credentials without hard-coding secrets or forcing every user to share one account?

Scalekit's connected-accounts model solves this for Apify. You use Apify's own userId as the stable identifier. Scalekit stores and refreshes the OAuth tokens per user (or per shared connector). Your Actor code simply checks whether the user is connected and then calls the service through Scalekit.

The result is an Actor you can safely offer to multiple customers or team members. Each person authorizes once, and every future run uses their own permissions and data.

Apify Actors need per-user accounts once you ship to customers

You can prototype an Apify Actor with one set of credentials and get useful work done quickly. The transition point comes the first time you want the same Actor to serve real users or customers:

  • Your Actor pulls public data but then needs to create or update pages in the customer's Notion.
  • You want to read a user's YouTube data or create playlists on their behalf.
  • The Actor needs to act inside services that require the user's own identity and permissions.

At that stage you need per-user authentication and authorization. Rolling your own OAuth, token storage, and refresh logic turns the Actor into a side project. Scalekit removes that work so you can keep focusing on the actual automation or data task.

Apify and Scalekit combine to deliver per-user identity

Apify provides the serverless runtime and a live-view port that can show users an interface while the run is active. Scalekit provides the OAuth layer and token management.

The key ideas:

  • Use Actor.getEnv().userId as the Scalekit identifier — no extra input fields required from the user.
  • Decide per connector whether it is shared (one account for everyone) or per-user (each Apify user connects their own service).
  • Before calling any third-party API, check if the connected account is active. If not, generate a magic link.
  • Optionally surface that link as a clean, branded page on the live-view port so users get a normal consent experience instead of a raw URL.
  • Once authorized, the Actor can call tools or the full Scalekit SDK while the session is scoped to that user.

This keeps your Actor code focused on the job while the identity and token handling live in one place.

An Actor writes results to each customer's Notion workspace

Imagine you built an Actor that scrapes public sources and then creates or updates pages in Notion. In testing you might use one integration token. That works until the first customer says "I want the output in my workspace."

With Scalekit you treat Notion as a per-user connector:

  • The Actor derives the identifier from the Apify userId.
  • On the first run it checks whether that identifier has an active Notion connection.
  • If not, it shows an authorization button on the live view (or returns a link).
  • After the customer completes the OAuth flow, every future run finds the token and writes inside their Notion account.

The same Actor can use a shared YouTube connection for read-only research data that doesn't need to be scoped per user. You choose per connector.

Serve the OAuth consent as an interactive page on the live view

Printing a raw magic link into logs or JSON creates friction. Apify Actors can run a small HTTP server on the live-view port. The Scalekit integration can serve a simple branded page that says "Authorize Notion" with a clear button.

The customer clicks, completes the flow in their browser, and the page updates to "Authorized — returning to task." The Actor can poll in the background and continue automatically once the connection is active.

It's a small detail, but it turns a copy-paste-and-wait step into something that feels like a normal product flow.

High-level code keeps your Actor logic focused

You don't manage tokens yourself. The shape looks like this (cleaned from the official cookbook):

import { Actor } from 'apify'; import { ScalekitClient } from '@scalekit-sdk/node'; const scalekit = new ScalekitClient({ envUrl: process.env.SCALEKIT_ENV_URL!, clientId: process.env.SCALEKIT_CLIENT_ID!, clientSecret: process.env.SCALEKIT_CLIENT_SECRET!, }); const { userId } = Actor.getEnv(); // Per-user identifier const notionIdentifier = userId; // Shared identifier for something everyone can use const youtubeIdentifier = 'shared-research'; async function ensureConnected(connectionName: string, identifier: string) { const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ connectionName, identifier, }); if (connectedAccount.status === 'ACTIVE') return; const { link } = await scalekit.actions.getAuthorizationLink({ connectionName, identifier, }); // Serve on live view or return the link console.log(`Authorize ${connectionName}:`, link); // Actor can poll until status is ACTIVE } await ensureConnected('notion', notionIdentifier); // now call Notion tools through Scalekit or the full SDK

If you later need more than session data (organization details, other Scalekit APIs, etc.), you can reach the full Scalekit SDK from inside the same authenticated context.

Getting started

The reference implementation shows the full pattern in one place:

https://github.com/scalekit-developers/agentkit-apify-actor-example

It includes:

  • Deriving the identifier from Apify's runtime
  • Handling both shared and per-user connectors
  • Serving the interactive consent page on the live view
  • Polling until authorization completes
  • Scoping actions to the authenticated user

Clone it, add your Scalekit and Apify credentials, and run it. The Actor will prompt for authorization on first use and then run with the correct user context thereafter.

Common issues

  • Connection stays pending — make sure the verification step is called after the user finishes OAuth.
  • Wrong data visible across users — confirm you're always passing the Apify userId (or another stable per-user value) as the Scalekit identifier.
  • Live view page not appearing — ensure the Actor is actually starting the HTTP server on the expected port.

More details are in the Scalekit cookbook and Apify documentation.

Benefits for Apify builders

Apify gives you a clean way to run tasks at scale. Scalekit gives you a clean way to give those tasks the right identity. You keep the Actor focused on the actual work (scrape this, transform that, write the result) while the auth and token management are handled in one place.

The outcome is an Actor you can confidently share with customers or run on behalf of different people without them ever seeing each other's data or credentials. This approach aligns with access control for multi-tenant AI agents, ensuring each user's data stays isolated.

What's next

Create a Scalekit account and set up your first connection at scalekit.com.

If you move fast with AI coding agents, the quickest way to add the integration is:

$ npx @scalekit-inc/cli setup $ pick your coding agent (Claude, Cursor, Copilot, Codex + any) $ claude "Help me add Scalekit AgentKit into this Apify Actor so each run uses the right user's connected accounts."

Try it out!

No items found.
Agent Auth Quickstart
On this page
Share this article
Agent Auth Quickstart

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 million Monthly Active Users
100 Monthly Active Organizations
1 SSO connection
1 SCIM connection
10K Connected Accounts
Unlimited Dev & Prod environments