Operated by Edamame Inc. · Tokyo · Manila · Kintone work since 2019
Journal

When Kintone lookup gets slow — scaling past 50,000 records

2026-04-18 · 8 min read · Tom Arai · Founder, Edamame Inc.

Once a Kintone app passes a few thousand records, lookups start to feel sluggish. At 10,000+ records, the slowness becomes unmistakable. At 50,000, users complain. This post covers specific techniques to keep Kintone lookups fast at scale, with real measurement data.

The short version

Native Kintone lookup slows down noticeably past 10,000 records. Causes: the records API's 10,000-offset limit and UI rendering cost. Solutions: pre-filter via query conditions, progressive loading in JavaScript, cursor API, or a purpose-built plugin. Verified implementation examples at 50,000-record scale included.

Why native lookups slow down

Kintone's lookup field fetches candidate records from a referenced app and presents them for user selection. The native flow:

  1. User clicks the lookup icon
  2. Kintone fetches candidate records via API
  3. Rendering the candidate list in a modal
  4. User selects, value is imported

Steps 2 and 3 are where the bottlenecks are.

API bottleneck: offset limit

Kintone REST API's /k/v1/records.json endpoint caps offset at 10,000. For apps with more than 10,000 records, the native API has no reliable way to reach records past that position.

With 20,000 or 50,000-record lookup target apps, you get the "this record should exist but search doesn't find it" symptom. This isn't a plugin or UI bug — it's an API spec limitation.

UI bottleneck: bulk load

The native lookup modal tries to load all candidate records at once. With high counts, browser rendering takes time and users see "several seconds between click and display."

Measured times (typical PC browser, corporate network):

RecordsTime-to-displayFeel
1,0000.3sInstant
5,0001.2sSlight wait
10,0003-5sClearly slow
30,00010s+Unusable
50,000Search breaks (offset)Native won't work

Solution 1: pre-filter via query conditions

Simplest approach: apply filters at lookup-invocation time. Even if your customer master app has 50,000 records, most order-entry workflows only need the current user's assigned customers.

In the Kintone lookup config, adding a condition like assigned_rep = LOGINUSER() shrinks the candidate pool dramatically and speeds things up.

Limits of this approach:

  • Business logic may be too complex for the condition syntax (e.g., customers whose last purchase was within 30 days AND rank is A or B)
  • Dynamic filters (depending on other field values) can't be expressed
  • Doesn't help for org-wide "shared customer master" use cases

Solution 2: progressive loading (JavaScript)

Fetch records incrementally as the user types. Empty until 2+ characters, then search API call, return 100 results, load more on scroll.

// Conceptual code — real implementations are more involved
searchInput.addEventListener('input', debounce(async (e) => {
  const query = e.target.value;
  if (query.length < 2) return;

  const records = await kintone.api('/k/v1/records', 'GET', {
    app: LOOKUP_APP_ID,
    query: `customer_name like "${query}" limit 100`
  });

  renderResults(records);
}, 300));

Key points:

  • Debounce: don't fire the API on every keystroke. Wait 300ms after typing stops.
  • Limit specified: max 100 per fetch. "Load more" button for additional results.
  • Query filter: pass search query directly to query parameter for server-side filtering.

But this still hits the offset limit. If your query matches more than 10,000 records, records beyond 10,000 still can't be reached.

Solution 3: cursor API for large-scale retrieval

Kintone's /k/v1/records/cursor.json endpoint bypasses the offset limit.

// 1. Create cursor
const cursor = await kintone.api('/k/v1/records/cursor', 'POST', {
  app: LOOKUP_APP_ID,
  query: `customer_name like "${query}"`,
  size: 500  // per-batch size
});

// 2. Fetch using cursor in a loop
let allRecords = [];
let next = true;
while (next) {
  const result = await kintone.api('/k/v1/records/cursor', 'GET', {
    id: cursor.id
  });
  allRecords = allRecords.concat(result.records);
  next = result.next;
}

Cursor API enables large-scale searches beyond the offset limit. Constraints: 10-minute cursor expiration, max 10 concurrent cursors per subdomain.

Solution 4: purpose-built plugin

Implementing the above three yourself requires significant engineering and ongoing maintenance. Especially when requirements include full-field search, subtable support, mobile support, result preview — it becomes non-trivial.

Purpose-built plugins like Smart Lookup have these implemented. Key features:

  • Full-text search across every field (not just the configured lookup-fetch fields)
  • Progressive loading (results render within 100ms of modal open)
  • Subtable lookup support
  • Mobile (/k/m/) parity with desktop UX
  • Cursor API internally, so record-count scaling is essentially unlimited

Actual measurements at 50,000 records

Real measurement data for kinplug's Smart Lookup plugin, tested against a 50,000-record test app:

OperationTime
Lookup modal initial render80-120ms
Search query → results displayed250-400ms
Load 100 more (scroll)150-250ms
Subtable 10-row lookup100-150ms (each)
Mobile parityComparable (network-dependent)

At 50k scale, native Kintone lookup is effectively unusable. A properly engineered plugin keeps the UX fluid.

Operational notes

Plan for record growth

An app that's 10,000 records at launch often passes 50,000 within a few years. "Currently fast enough" isn't the right evaluation — design for growth.

Index tuning isn't available

Kintone has internal indexes you can't customize. When a query is slow, you can't "add an index on this field." Alternative: split apps or restructure the lookup target.

Subtable lookups are particularly expensive

Each lookup field in a subtable multiplies rendering cost by row count × candidate-record count. A 10-row subtable with 10,000-record lookup targets quickly exceeds native capability. A plugin is practically mandatory.

Summary

Kintone lookup slowness comes from the API's offset limit plus UI rendering cost. Native is fine for small datasets, but at 10,000+ records you need a strategy. Pre-filtering, progressive loading, cursor API, or purpose-built plugins — using at least one keeps even 50,000-record lookups fast.

Related

Next step

Try kinplug free for 14 days

No credit card. Sign in with Google or Microsoft, enter your Kintone subdomain, and go live in 90 seconds.

Start free See plugins
Get started

14 days, every feature,
no credit card.

Sign in with Google or Microsoft, enter your Kintone subdomain, install the plugin. Live in 90 seconds.