Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with Marketo

Integrate Bolt.new with Marketo (Adobe) using the Marketo REST API with OAuth 2.0 client credentials. Get your API credentials from Marketo Admin → Integration → LaunchPoint, build a Next.js API route that fetches an access token, and call the REST API to manage leads, query campaign performance, and sync form submissions. Requires an active Marketo subscription. Rate limit is 50,000 calls per day.

What you'll learn

  • How to create a Marketo LaunchPoint service and obtain REST API credentials
  • How to implement Marketo's OAuth 2.0 client credentials flow to get access tokens
  • How to create and update Marketo leads via the REST API with upsert operations
  • How to query lead activity data and campaign performance metrics
  • How to sync Bolt form submissions to Marketo leads with proper field mapping
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read40 minutesMarketingApril 2026RapidDev Engineering Team
TL;DR

Integrate Bolt.new with Marketo (Adobe) using the Marketo REST API with OAuth 2.0 client credentials. Get your API credentials from Marketo Admin → Integration → LaunchPoint, build a Next.js API route that fetches an access token, and call the REST API to manage leads, query campaign performance, and sync form submissions. Requires an active Marketo subscription. Rate limit is 50,000 calls per day.

Building Marketo Marketing Automation Features in Bolt.new Apps

Marketo's REST API exposes the full marketing automation platform programmatically: lead database management, campaign triggers, email performance metrics, scoring rules, and activity history. For Bolt.new developers building B2B SaaS apps or marketing tools, the most common integration scenarios are syncing leads from web forms into the Marketo database, triggering campaign workflows when users reach specific milestones, and building custom reporting dashboards that surface Marketo data in a more relevant context than Marketo's native analytics.

Marketo's API authentication uses the OAuth 2.0 client credentials flow — sometimes called two-legged OAuth — where your server exchanges a client ID and secret for a time-limited access token. There is no user login required. This means the integration works purely server-to-server, which aligns well with Bolt's API route pattern. You request a token, cache it for its one-hour lifetime, and include it in every API request.

Marketo is an enterprise product with subscription pricing — integrating it requires an active Marketo subscription where the administrator can create a LaunchPoint service. If you are evaluating whether to build a Marketo integration, confirm that your organization has Marketo access and that your Marketo admin can create an API user and LaunchPoint service before investing time in the implementation. The daily call limit is 50,000 API calls, which is generous for most integration patterns.

Integration method

Bolt Chat + API Route

Bolt generates the Marketo integration through conversation — you describe the lead management or campaign analytics features you need and Bolt writes the Next.js API routes and React components. Marketo's REST API uses OAuth 2.0 client credentials (no user login required), so the full integration can be developed and partially tested in Bolt's WebContainer. All API calls must go through server-side Next.js routes since the client ID and secret must never appear in browser code.

Prerequisites

  • An active Marketo subscription (trial or paid) with administrator access to create LaunchPoint services
  • A Marketo API-Only user account created by your Marketo admin (Admin → Security → Users → Invite User → select API Only)
  • A LaunchPoint service created in Marketo Admin → Integration → LaunchPoint with the API-Only user assigned
  • Your Marketo REST API endpoint URL (found in Admin → Integration → Web Services → REST API → Endpoint)
  • Client ID and Client Secret from the LaunchPoint service detail view

Step-by-step guide

1

Create a Marketo LaunchPoint service and get API credentials

Marketo's API access is managed through the LaunchPoint service registry. Before you can make any API calls, a Marketo administrator must create an API-only user account and a corresponding LaunchPoint service that generates the client credentials. Step 1: Create an API-only user. In Marketo, go to Admin → Security → Users → Invite User. Enter an email address for the API user (can be a service account email), check 'API Only', and assign the appropriate role — at minimum the 'Read-Write Lead' and 'Read-Write Assets' permissions. Complete the invitation. Step 2: Create a LaunchPoint service. Go to Admin → Integration → LaunchPoint → New → New Service. Give the service a name (e.g., 'Bolt App Integration'), set the service type to 'Custom', and select the API-only user you just created. Save the service. Step 3: Get credentials. On the LaunchPoint services list, find your new service and click 'View Details'. You will see the Client ID and Client Secret. Copy both. Step 4: Find your REST API endpoint. Go to Admin → Integration → Web Services. Under the REST API section, you will see the Endpoint URL — it looks like https://[instance-id].mktorest.com/rest. Copy this URL. You will also need the Identity URL shown there, which is used for token requests. Store all four values in your Bolt project's .env.local file.

Bolt.new Prompt

Create a Next.js app with Marketo REST API integration. Add MARKETO_CLIENT_ID, MARKETO_CLIENT_SECRET, MARKETO_REST_ENDPOINT, and MARKETO_IDENTITY_URL to .env.local. Create a lib/marketo.ts file with a getMarketoToken() function that fetches an OAuth access token from the Identity URL using client_id, client_secret, and grant_type=client_credentials. Cache the token with its expiry time.

Paste this in Bolt.new chat

.env.local
1// .env.local
2MARKETO_CLIENT_ID=your_client_id_here
3MARKETO_CLIENT_SECRET=your_client_secret_here
4MARKETO_REST_ENDPOINT=https://your-instance-id.mktorest.com/rest
5MARKETO_IDENTITY_URL=https://your-instance-id.mktorest.com/identity

Pro tip: The Marketo Identity URL and REST Endpoint share the same instance ID subdomain (e.g., 123-ABC-456.mktorest.com). If you have the REST endpoint, the Identity URL is the same domain with /identity as the path.

Expected result: All four Marketo credentials are stored in .env.local and the getMarketoToken() function successfully returns an access token when called from a server-side route.

2

Build the token utility and lead upsert API route

The Marketo token request uses the Identity URL with GET parameters: grant_type=client_credentials, client_id, and client_secret. Despite being a GET request (not POST like most OAuth implementations), Marketo uses GET for token requests — this is a Marketo-specific quirk worth noting. The token response includes access_token and expires_in (seconds, typically 3600). Cache the token in server memory alongside its expiry timestamp. Check expiry before every API call and refresh if within 60 seconds of expiry. In a Next.js serverless environment, each function invocation may have its own memory, so the cache may not persist between requests — implement either a fast token cache (Redis or Upstash) for production or simply accept the small overhead of re-fetching tokens. The most fundamental Marketo operation is the lead upsert: creating a new lead if none exists with that email, or updating an existing lead's fields. Use POST /rest/v1/leads.json with action: 'createOrUpdate', lookupField: 'email', and an input array of lead objects. This single endpoint handles both creates and updates with deduplication on email address. The response includes a result array with each lead's id, status ('created', 'updated', or 'skipped'), and any errors.

Bolt.new Prompt

Build the Marketo token utility and lead upsert API route. The lib/marketo.ts utility should export getMarketoToken() with in-memory caching. Create app/api/marketo/leads/upsert/route.ts that accepts POST with an array of lead objects (email, firstName, lastName, company, title) and calls the Marketo createOrUpdate leads endpoint. Return the created/updated lead IDs and statuses.

Paste this in Bolt.new chat

lib/marketo.ts
1// lib/marketo.ts
2let tokenCache: { token: string; expiresAt: number } | null = null;
3
4export async function getMarketoToken(): Promise<string> {
5 if (tokenCache && Date.now() < tokenCache.expiresAt - 60_000) {
6 return tokenCache.token;
7 }
8
9 const identityUrl = process.env.MARKETO_IDENTITY_URL!;
10 const clientId = process.env.MARKETO_CLIENT_ID!;
11 const clientSecret = process.env.MARKETO_CLIENT_SECRET!;
12
13 // Note: Marketo token request uses GET (not POST) with query parameters
14 const response = await fetch(
15 `${identityUrl}/oauth/token?grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}`
16 );
17
18 if (!response.ok) throw new Error(`Marketo token request failed: ${response.status}`);
19
20 const data = await response.json();
21 if (data.error) throw new Error(`Marketo auth error: ${data.error_description}`);
22
23 tokenCache = {
24 token: data.access_token,
25 expiresAt: Date.now() + data.expires_in * 1000,
26 };
27 return tokenCache.token;
28}
29
30export async function marketoFetch(
31 path: string,
32 options: RequestInit = {}
33): Promise<Response> {
34 const token = await getMarketoToken();
35 const baseUrl = process.env.MARKETO_REST_ENDPOINT!;
36 return fetch(`${baseUrl}${path}`, {
37 ...options,
38 headers: {
39 Authorization: `Bearer ${token}`,
40 'Content-Type': 'application/json',
41 ...(options.headers ?? {}),
42 },
43 });
44}
45
46// app/api/marketo/leads/upsert/route.ts
47import { NextRequest, NextResponse } from 'next/server';
48import { marketoFetch } from '@/lib/marketo';
49
50interface MarketoLead {
51 email: string;
52 firstName?: string;
53 lastName?: string;
54 company?: string;
55 title?: string;
56 [key: string]: unknown;
57}
58
59export async function POST(request: NextRequest) {
60 const body = await request.json();
61 const leads: MarketoLead[] = Array.isArray(body) ? body : [body];
62
63 const response = await marketoFetch('/v1/leads.json', {
64 method: 'POST',
65 body: JSON.stringify({
66 action: 'createOrUpdate',
67 lookupField: 'email',
68 input: leads,
69 }),
70 });
71
72 if (!response.ok) {
73 return NextResponse.json({ error: `Marketo API error: ${response.status}` }, { status: response.status });
74 }
75
76 const data = await response.json();
77
78 if (!data.success) {
79 return NextResponse.json({ error: data.errors }, { status: 400 });
80 }
81
82 return NextResponse.json({
83 results: data.result,
84 requestId: data.requestId,
85 });
86}

Pro tip: Marketo limits lead upsert batches to 300 leads per request. If you need to sync more than 300 leads at once, split the input array into chunks of 300 and make multiple sequential requests.

Expected result: Calling /api/marketo/leads/upsert with a lead object containing an email address successfully creates or updates the lead in Marketo and returns the lead ID and status.

3

Query leads and build a lead management dashboard

Querying leads in Marketo requires understanding the filter type system. The GET /rest/v1/leads.json endpoint accepts filterType (the field to filter on) and filterValues (comma-separated values). The most common filter types are email (by email address), id (by Marketo lead ID), and leadPartitionId (all leads in a partition). Custom fields can also be used as filter types if they are indexed. The fields parameter controls which lead attributes are returned — always specify exactly the fields you need rather than returning all fields, as Marketo lead records can have hundreds of custom fields and returning all of them significantly increases response size and latency. For dashboard use cases that require sorting and paging across large lead databases, the bulk extract API is more appropriate than the filter API. However, bulk extract is asynchronous (you submit a job, wait for it to complete, then download the result file) and adds significant complexity. For dashboards showing the top N leads by score, using the filter API with the lead score filter type and a reasonable limit is simpler and works well for most use cases. Marketo's activity types API provides the historical record of what leads have done: email opens, web page visits, form fills, score changes. To get a lead's activity, first call /rest/v1/activities/leadchanges.json or /rest/v1/activities.json with the lead ID and a paging token. Activity queries require a paging token obtained from /rest/v1/activities/pagingtoken.json, which is a date-based starting point.

Bolt.new Prompt

Create a Marketo lead query route at /api/marketo/leads/query that accepts an email address or list of email addresses and returns the lead data including score, email, name, company, and last activity date. Build a lead search component that calls this route and displays matching leads in a table with a link to the lead's detail page.

Paste this in Bolt.new chat

app/api/marketo/leads/query/route.ts
1// app/api/marketo/leads/query/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { marketoFetch } from '@/lib/marketo';
4
5export async function GET(request: NextRequest) {
6 const { searchParams } = new URL(request.url);
7 const emails = searchParams.get('emails'); // comma-separated
8 const filterType = searchParams.get('filterType') ?? 'email';
9
10 if (!emails) {
11 return NextResponse.json({ error: 'emails query parameter is required' }, { status: 400 });
12 }
13
14 const fields = [
15 'id', 'email', 'firstName', 'lastName', 'company', 'title',
16 'leadScore', 'leadStatus', 'updatedAt', 'createdAt',
17 ].join(',');
18
19 const response = await marketoFetch(
20 `/v1/leads.json?filterType=${filterType}&filterValues=${encodeURIComponent(emails)}&fields=${fields}`
21 );
22
23 if (!response.ok) {
24 return NextResponse.json({ error: `Marketo API error: ${response.status}` }, { status: response.status });
25 }
26
27 const data = await response.json();
28
29 if (!data.success) {
30 return NextResponse.json({ error: data.errors }, { status: 400 });
31 }
32
33 return NextResponse.json({
34 leads: data.result,
35 total: data.result?.length ?? 0,
36 requestId: data.requestId,
37 });
38}

Pro tip: Always include the 'fields' parameter when querying Marketo leads. Omitting it returns all fields, including hundreds of empty custom fields, resulting in very large responses that are slow to process.

Expected result: Querying /api/marketo/leads/query?emails=user@example.com returns the lead record with score, contact info, and timestamps from Marketo.

Common use cases

Lead capture form sync

When a user submits a contact or sign-up form in your Bolt app, automatically create or update a lead in Marketo with their contact information, company, and any custom fields that map to your lead scoring criteria. Marketo's lead scoring and nurture workflows activate automatically based on the lead's attributes.

Bolt.new Prompt

Add a lead sync to my contact form. When the form is submitted with name, email, company, and job title, call /api/marketo/leads/upsert to create or update the lead in Marketo. Map the form fields to Marketo's standard fields: Email, FirstName, LastName, Company, Title. Return success or a specific error if the email is already in a suppression list.

Copy this prompt to try it in Bolt.new

Lead scoring dashboard

Build an internal dashboard showing your top leads ranked by Marketo lead score, with each lead's recent activity (email opens, web visits, form fills) listed alongside their score. Sales reps can prioritize outreach without logging into Marketo directly.

Bolt.new Prompt

Create a lead scoring dashboard page. Fetch leads from the Marketo API filtered by lead score greater than 50, sorted by score descending, with a limit of 50. Display them in a table with columns for name, email, company, lead score, and last activity date. Add a 'View in Marketo' link for each lead.

Copy this prompt to try it in Bolt.new

Campaign performance analytics

Surface email campaign metrics — open rate, click rate, bounce rate, unsubscribes — from Marketo into a custom analytics page that also shows revenue attribution from your CRM. Marketing teams can see performance without switching between tools.

Bolt.new Prompt

Build a Marketo campaign analytics page. Fetch the list of recent email programs from the Marketo API and for each program fetch the email performance summary with sent count, open rate, click rate, and unsubscribe rate. Display the data as a table with sortable columns and sparkline trend indicators for open and click rates.

Copy this prompt to try it in Bolt.new

Troubleshooting

Token request returns 401 with error 'unauthorized' or '1000: Access token invalid'

Cause: The client ID or client secret is incorrect, the LaunchPoint service is not active, or the API-only user account has been deactivated or is not assigned to the service.

Solution: In Marketo Admin → Integration → LaunchPoint, click 'View Details' on your service to verify the client ID shown there matches your MARKETO_CLIENT_ID. Check that the API-only user account assigned to the service is active (Admin → Security → Users). If credentials look correct, try creating a new LaunchPoint service and generating new credentials.

Lead upsert returns success:false with error '1004: Lead not found' even for new leads

Cause: Using action: 'updateOnly' instead of 'createOrUpdate' — updateOnly fails if the lead does not exist. Alternatively, the lookupField value specified is not indexed in Marketo.

Solution: Always use action: 'createOrUpdate' for form sync scenarios. If a lead with the lookup email exists, it will be updated; if not, a new lead is created. The 'updateOnly' action is only appropriate when you know the lead already exists and want to prevent accidental creation.

typescript
1// Use createOrUpdate to handle both new and existing leads:
2body: JSON.stringify({
3 action: 'createOrUpdate', // Not 'updateOnly'
4 lookupField: 'email',
5 input: leads,
6})

API returns 413 or times out when trying to sync a large number of leads

Cause: The Marketo REST API enforces a 300-lead maximum per request for the lead upsert endpoint. Sending more than 300 leads in a single request causes an error.

Solution: Split the leads array into chunks of 300 and process them sequentially or in controlled batches. Add a small delay between batches to avoid rate limit issues on high-volume syncs.

typescript
1// Chunk leads into batches of 300:
2async function upsertLeadsBatch(leads: MarketoLead[]) {
3 const chunkSize = 300;
4 const results = [];
5 for (let i = 0; i < leads.length; i += chunkSize) {
6 const chunk = leads.slice(i, i + chunkSize);
7 const result = await marketoFetch('/v1/leads.json', {
8 method: 'POST',
9 body: JSON.stringify({ action: 'createOrUpdate', lookupField: 'email', input: chunk }),
10 });
11 results.push(await result.json());
12 if (i + chunkSize < leads.length) {
13 await new Promise(resolve => setTimeout(resolve, 100)); // Small delay between batches
14 }
15 }
16 return results;
17}

Best practices

  • Cache Marketo access tokens in memory with their expiry time — tokens last one hour and re-requesting them unnecessarily reduces your 50,000 daily API call budget.
  • Always specify the 'fields' parameter in lead queries — returning all fields by default includes hundreds of potentially empty custom fields and significantly increases response size.
  • Use 'createOrUpdate' action for all form-to-lead sync operations — it handles both new leads and returning contacts without requiring separate create and update logic.
  • Store MARKETO_CLIENT_ID and MARKETO_CLIENT_SECRET in server-side environment variables only — never prefix them with NEXT_PUBLIC_ as that would expose them in the browser bundle.
  • Map your application's custom data to Marketo custom fields using the API field names (not the display names) — field names are case-sensitive in Marketo's REST API.
  • Keep track of your daily API call usage through Marketo Admin → Integration → Web Services which shows current usage — hitting the 50,000 daily limit causes all API calls to return 606 errors until midnight.
  • For high-volume scenarios (thousands of leads per day), consider Marketo's bulk import API as an alternative to the transactional upsert API — bulk import is asynchronous but more efficient and does not count against the daily REST API call limit.

Alternatives

Frequently asked questions

Can I test the Marketo integration in Bolt's preview before deploying?

Yes. Marketo's REST API uses client credentials OAuth (no user login required) and all calls are outbound HTTP requests from your server-side API routes. Both work in Bolt's WebContainer development preview. The only feature that requires deployment is if you want Marketo to push webhook events to your app — Marketo can send webhooks for lead score changes and other events, but the webhook endpoint must be a deployed public URL.

Does Bolt.new have a native Marketo integration?

No. Marketo is not one of Bolt's native connectors. You integrate it by building Next.js API routes that call the Marketo REST API. Bolt's AI can generate the integration code when you describe your lead management or campaign analytics requirements.

What Marketo plan or role do I need to create API credentials?

You need Marketo administrator access to create an API-only user and a LaunchPoint service. Standard users cannot access the Admin → Integration → LaunchPoint menu. If you are not the Marketo admin, you will need to request that your admin create a LaunchPoint service and share the Client ID and Client Secret with you.

What is the Marketo REST API daily call limit?

The standard limit is 50,000 API calls per day, which resets at midnight in your Marketo instance's timezone. Enterprise contracts may have higher limits. You can monitor current usage in Admin → Integration → Web Services. Exceeding the limit returns error code 606 for all subsequent calls until the daily reset.

Can I use the Marketo API to send emails or trigger campaigns?

Yes. You can trigger campaign execution using POST /rest/v1/campaigns/{id}/trigger.json with a lead ID. This invokes a trigger-based Smart Campaign that has the 'Campaign is Requested (via Web Service API)' trigger. You can also schedule batch campaigns using POST /rest/v1/campaigns/{id}/schedule.json. For transactional single emails, the Transactional Email endpoint /rest/v1/email/sendSample.json is available on some Marketo plans.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.