creating-an-endpoint

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Creating an endpoint

创建端点

This skill walks through creating a new endpoint with the right configuration. Endpoints expose saved HogQL or insight queries as callable HTTP routes — the configuration choices made at creation time determine cost, latency, and how callers integrate.
The materialisation deep-dive lives at
references/materializing.md
. Pull it in when the materialisation decision is non-obvious.
本指南将逐步介绍如何创建配置正确的新端点。端点可将已保存的HogQL或洞察查询暴露为可调用的HTTP路由——创建时做出的配置选择将决定成本、延迟以及调用方的集成方式。
关于物化的深入说明请查看
references/materializing.md
。当物化决策不明确时,可参考该文档。

When to use this skill

何时使用本指南

  • "Create an endpoint for [query]"
  • "Expose this insight as an API"
  • "Help me turn this HogQL into a callable endpoint"
  • A new caller (mobile app, customer-facing dashboard, downstream pipeline) needs PostHog data and the user is choosing how to deliver it
  • "为[查询]创建一个端点"
  • "将此洞察暴露为API"
  • "帮我将这个HogQL转换为可调用的端点"
  • 新的调用方(移动应用、面向客户的仪表板、下游管道)需要PostHog数据,且用户正在选择数据交付方式

Decisions to make in order

需依次做出的决策

1. Should this even be an endpoint?

1. 是否真的需要创建端点?

Endpoints are right when:
  • An external system (someone else's code) needs to call PostHog for data
  • The query is stable — not exploratory analysis
  • The shape is reusable — same query with different parameters
Endpoints are wrong when:
  • An internal PostHog dashboard or insight needs the data — use the insight directly; an endpoint only adds an external API surface you don't need internally
  • One-off, exploratory analysis — use the
    execute-sql
    tool (or the SQL editor) directly
Heavy aggregation is not a reason to avoid an endpoint. Endpoints are themselves saved queries, and a heavy, frequently-called aggregation is often the best case for an endpoint with materialisation turned on.
If the user is unsure, ask what's calling the endpoint and what shape they expect.
适合创建端点的场景:
  • 外部系统(第三方代码)需要调用PostHog获取数据
  • 查询是稳定的——不是探索性分析
  • 数据格式具有可复用性——相同查询可传入不同参数
不适合创建端点的场景:
  • PostHog内部仪表板或洞察需要数据——直接使用洞察即可;创建端点只会增加不必要的外部API接口
  • 一次性探索性分析——直接使用
    execute-sql
    工具(或SQL编辑器)
大量聚合操作不是避免创建端点的理由。端点本身就是已保存的查询,对于频繁调用的复杂聚合操作,启用物化的端点往往是最佳选择。
若用户不确定,可询问调用端点的系统是什么,以及期望的数据格式。

2. Pick a name

2. 选择名称

Names are URL-safe (letters, numbers, hyphens, underscores), start with a letter, max 128 chars, must be unique within the project. Lean toward:
  • Descriptive over generic
    weekly_active_users_by_org
    over
    metrics
  • Snake_case — matches how the name appears in code paths and URLs
  • No version in the name — versions are managed by the endpoint itself
  • No "endpoint" in the name — redundant
The name appears in the URL:
/api/projects/{team_id}/endpoints/{name}/run
. It's not trivially renameable later (callers depend on the path) — get it right at creation.
名称需符合URL安全规范(字母、数字、连字符、下划线),以字母开头,最长128字符,且在项目内必须唯一。建议遵循以下原则:
  • 描述性优先于通用性——使用
    weekly_active_users_by_org
    而非
    metrics
  • 采用蛇形命名法(Snake_case)——与代码路径和URL中的名称格式一致
  • 名称中不包含版本号——版本由端点自身管理
  • 名称中不包含"endpoint"——属于冗余信息
名称会出现在URL中:
/api/projects/{team_id}/endpoints/{name}/run
。后续修改名称并非易事(调用方依赖该路径)——因此创建时务必确定合适的名称。

3. Pick the query kind

3. 选择查询类型

Two options exist:
  • HogQL (
    HogQLQuery
    ) — raw SQL written by the user. Variables defined via
    {variables.x}
    syntax, matched on
    code_name
    . Recommended for new endpoints when the caller cares about the exact column shape of the response.
  • Insight — wraps an existing insight definition. Best supported for
    TrendsQuery
    ,
    LifecycleQuery
    , and
    RetentionQuery
    : these can be materialised, and the breakdown can act as a variable (Trends and Retention only; Lifecycle has no breakdown). Other insight kinds such as
    FunnelsQuery
    can run inline but cannot be materialised and don't expose breakdown variables — rewrite those as HogQL if you need either.
HogQL is the more flexible choice. Pick insight only when the user is genuinely re-publishing an existing insight (see "Creating from an existing insight" below) rather than building a new query.
有两种选项:
  • HogQL
    HogQLQuery
    )——用户编写的原始SQL。通过
    {variables.x}
    语法定义变量,匹配
    code_name
    。当调用方关心响应的精确列格式时,建议为新端点选择此类型。
  • 洞察——封装现有洞察定义。对
    TrendsQuery
    LifecycleQuery
    RetentionQuery
    支持最佳:这些查询可被物化,且细分维度可作为变量(仅适用于Trends和Retention;Lifecycle无细分维度)。其他洞察类型如
    FunnelsQuery
    可直接运行,但无法被物化且不暴露细分维度变量——若需要物化或变量功能,需将其重写为HogQL。
HogQL是更灵活的选择。仅当用户确实需要重新发布现有洞察时(见下文“从现有洞察创建”),才选择洞察类型,而非构建新查询。

4. Decide which inputs become variables

4. 确定哪些输入作为变量

Anything that should change per-caller goes in variables; the rest is hard-coded in the query.
For HogQL endpoints, variables are declared in the query payload with
code_name
,
type
, and
default
. Each execution call passes
{ "variables": { "<code_name>": value } }
.
Common patterns:
  • Time windows:
    date_from
    ,
    date_to
    , or a single
    lookback_days
    integer
  • Identity filters:
    user_id
    ,
    account_id
    ,
    team_id
  • Pagination control beyond
    limit
    /
    offset
    (these are first-class on the run endpoint already)
For insight endpoints, the breakdown property acts as the variable (Trends and Retention only — Lifecycle has no breakdown). Pass the breakdown property name as the key.
date_from
/
date_to
are accepted as variables only on non-materialised insight endpoints — a materialised endpoint bakes its date range into the view, so callers can't shift the window.
Avoid:
  • Variables that change the shape of the result — keep the columns stable. If callers need fundamentally different result shapes, ship separate endpoints.
  • Variables that bypass safety — don't expose a
    where_clause
    variable that lets callers inject arbitrary SQL.
所有需要随调用方变化的内容都应设为变量;其余内容硬编码到查询中。
对于HogQL端点,变量在查询负载中通过
code_name
type
default
声明。每次执行调用需传入
{ "variables": { "<code_name>": value } }
常见模式:
  • 时间窗口:
    date_from
    date_to
    ,或单个整数变量
    lookback_days
  • 身份过滤:
    user_id
    account_id
    team_id
  • 超出
    limit
    /
    offset
    的分页控制(这些是运行端点的原生参数)
对于洞察端点,细分维度属性作为变量(仅适用于Trends和Retention——Lifecycle无细分维度)。传入细分维度属性名称作为键。
date_from
/
date_to
仅在非物化洞察端点中可作为变量——物化端点会将日期范围固化到视图中,调用方无法调整时间窗口。
需避免:
  • 会改变结果格式的变量——保持列稳定。若调用方需要完全不同的结果格式,应创建单独的端点。
  • 存在安全风险的变量——不要暴露
    where_clause
    变量,以免调用方注入任意SQL。

Creating from an existing insight

从现有洞察创建

There's no server-side "make an endpoint from insight N" operation. To do it: read the insight's query (via the insight tools), pass that query to
endpoint-create
, and set
derived_from_insight
to the insight's short id so the origin is recorded. The endpoint then owns its own copy of the query — later edits to the insight don't propagate. Starting from scratch instead? Build the query first with the insight /
sql-variables
tools, then create the endpoint from it.
目前没有服务器端“从洞察N创建端点”的操作。实现方法:通过洞察工具读取洞察的查询,将该查询传入
endpoint-create
,并将
derived_from_insight
设为洞察的短ID,以记录来源。此时端点会拥有自己的查询副本——后续对洞察的编辑不会同步到端点。若要从零开始构建?先使用洞察/
sql-variables
工具构建查询,再基于该查询创建端点。

5. Set
data_freshness_seconds

5. 设置
data_freshness_seconds

This one field does two jobs, so set it deliberately:
  1. Cache TTL — results are served from cache until they're this many seconds old.
  2. Materialisation refresh frequency — on a materialised endpoint, this is also how often the warehouse recomputes the materialised view.
So a lower value means fresher data and more frequent recompute/refresh cost; a higher value is cheaper on both counts but staler.
The value must be one of a fixed set:
900
(15 min),
1800
(30 min),
3600
(1 h),
21600
(6 h),
43200
(12 h),
86400
(24 h, default),
604800
(7 d). There is no sub-15-minute option —
900
is the floor.
data_freshness_seconds
When to pick it
900–1800Freshest available — dashboards where staleness is visible
3600–43200Most cases — fresh enough for product usage, cheap to recompute
86400–604800Reports, weekly/daily metrics, anything aggregated over long periods
Bias toward higher values unless the user explicitly needs fresher data. On a materialised endpoint, remember this also sets the refresh cadence.
该字段承担两项功能,需谨慎设置:
  1. 缓存TTL——结果会从缓存中返回,直到缓存数据超过该秒数。
  2. 物化刷新频率——对于物化端点,该值同时决定数据仓库重新计算物化视图的频率。
因此,值越小意味着数据越新鲜,但重新计算/刷新的成本越高;值越大则成本越低,但数据越陈旧。
该值必须是以下固定值之一:
900
(15分钟)、
1800
(30分钟)、
3600
(1小时)、
21600
(6小时)、
43200
(12小时)、
86400
(24小时,默认值)、
604800
(7天)。没有低于15分钟的选项——
900
是最小值。
data_freshness_seconds
适用场景
900–1800最实时的数据——对数据陈旧度敏感的仪表板
3600–43200大多数场景——数据新鲜度满足产品使用需求,重新计算成本低
86400–604800报表、周/日度指标、任何长期聚合的数据
除非用户明确需要更实时的数据,否则优先选择较大的值。对于物化端点,需记住该值同时设置了刷新频率。

6. Decide on day-one materialisation

6. 决定是否在创建初期启用物化

See
references/materializing.md
for the full decision tree. Short version:
  • Recommend materialisation when the endpoint will be called frequently, latency matters, and the user can tolerate staleness equal to the refresh interval (typically 5-15 minutes for scheduled materialisation, or hourly).
  • Skip materialisation for low-traffic endpoints, exploratory new endpoints (you don't know yet if it'll get called), and queries where freshness is critical.
If unsure, create unmaterialised and add
is_materialized: true
later once usage stabilises. That avoids paying for materialisation on a query nobody ends up calling.
完整的决策树请查看
references/materializing.md
。简要说明:
  • 建议启用物化:当端点会被频繁调用、延迟至关重要,且用户可接受与刷新间隔相当的数据陈旧度(通常计划物化的间隔为5-15分钟,或每小时)。
  • 不启用物化:低流量端点、新的探索性端点(尚不确定是否会被调用),以及对数据新鲜度要求极高的查询。
若不确定,可先创建非物化端点,待使用稳定后再添加
is_materialized: true
。这样可避免为无人调用的查询支付物化成本。

Workflow

工作流程

  1. Confirm the use case (step 1 above). If it's not actually a fit for an endpoint, recommend the alternative.
  2. Agree a name with the user.
  3. Walk through the query — confirm it's HogQL or insight, and that the columns/shape make sense.
  4. Identify what should be a variable. Show the user the variable declaration syntax.
  5. Pick
    data_freshness_seconds
    based on the user's freshness requirement (ask if not clear) — remembering it also sets the materialisation refresh cadence.
  6. Make the materialisation call. If on the fence, ship without and revisit later.
  7. Call
    endpoint-create
    with the agreed config.
  8. Confirm by calling
    endpoint-run
    with a sample payload to verify the response shape.
  9. Hand off to
    consuming-endpoints-from-client-code
    if the user is about to wire it up.
  1. 确认使用场景(上述第1步)。若不适合创建端点,推荐替代方案。
  2. 与用户商定端点名称。
  3. 梳理查询——确认是HogQL还是洞察类型,且列/格式合理。
  4. 确定哪些内容应设为变量。向用户展示变量声明语法。
  5. 根据用户的新鲜度需求选择
    data_freshness_seconds
    (若不明确则询问)——记住该值同时设置物化刷新频率。
  6. 做出物化决策。若犹豫不决,先不启用,后续再重新考虑。
  7. 使用商定的配置调用
    endpoint-create
  8. 通过调用
    endpoint-run
    并传入示例负载来验证响应格式,确认创建成功。
  9. 若用户即将进行客户端代码集成,可转交至
    consuming-endpoints-from-client-code
    指南。

Example interaction

示例交互

text
User: "I want to expose our monthly active users count as an API
       for our analytics partner"

Agent:
- "Quick check: is the partner going to call this on demand, or
   should we be pushing data to them? Endpoints are pull-only."
- User: "On demand"
- "Got it. A few choices:
   - Name: how about monthly_active_users? Lowercase, descriptive,
     no version baggage.
   - The query — is it HogQL or are you exposing an existing insight?
     HogQL gives you exact column control; insight reuses what you've
     already built.
   - What should the partner be able to filter by? Org, time window,
     both?
   - How fresh? If MAU drifts within a day is fine, we can cache
     results for 1-24 hours and serve almost everything from cache.
   - Materialisation: if the partner is going to hit this many times
     per day, materialising it would make responses near-instant.
     The trade-off is ~10-15 minute staleness.

   What's your starting point on these?"

[user picks: HogQL, lookback_days + org_id variables, 3600s TTL,
 not materialised yet]

- endpoint-create monthly_active_users {query, variables, ...}
- endpoint-run with sample payload {org_id: "test", lookback_days: 30}
- "Created and tested. Want help wiring up the client code?
   That's consuming-endpoints-from-client-code."
text
用户:"我想将我们的月活跃用户数暴露为API,供我们的分析合作伙伴使用"

Agent:
- "快速确认:合作伙伴是按需调用,还是我们需要主动推送数据?端点仅支持拉取模式。"
- 用户:"按需调用"
- "明白了。有几个选项:
   - 名称:使用monthly_active_users怎么样?小写、描述性强,无版本冗余。
   - 查询类型——是HogQL还是暴露现有洞察?HogQL可让你精确控制列;洞察类型可复用已构建的内容。
   - 合作伙伴需要按什么维度过滤?组织、时间窗口,还是两者都要?
   - 数据新鲜度要求?如果月活跃用户数在一天内的波动可接受,我们可以将结果缓存1-24小时,几乎所有请求都从缓存返回。
   - 物化:如果合作伙伴每天多次调用该端点,启用物化可让响应近乎即时。代价是数据会有~10-15分钟的延迟。

   你对这些选项的初步想法是什么?"

[用户选择:HogQL,lookback_days + org_id变量,3600秒TTL,暂不启用物化]

- 调用endpoint-create创建monthly_active_users {query, variables, ...}
- 使用示例负载{org_id: "test", lookback_days: 30}调用endpoint-run
- "已创建并测试完成。需要帮助编写客户端代码吗?可参考consuming-endpoints-from-client-code指南。"

Important notes

重要注意事项

  • The name lives in the URL. Changing it later requires migrating callers. Pick well.
  • HogQL endpoints are more flexible than insight endpoints. Default to HogQL unless the user has a specific reason to wrap an existing insight.
  • Variables with no default fail at call time. Always set defaults during creation so the endpoint is testable from the playground without specifying every variable.
  • Materialised endpoints require all variables to be passed. Calls without them are rejected — this is intentional (security: prevents returning unfiltered data). Pair the materialisation recommendation with a note to the user about which variables become required. (Optional/partial variables on materialised endpoints are a known limitation the PostHog team plans to lift — if it's blocking the user, nudge them via the
    agent-feedback
    tool.)
  • Don't enable materialisation on a query that isn't eligible. Use
    endpoints-materialization-preview
    first to confirm eligibility and see the rejection reason if any.
  • Endpoints are not stable forever. When the user changes the query, a new version is created automatically (the old version stays accessible via
    ?version=N
    ).
    data_freshness_seconds
    and materialisation are per-version. Adjust as the endpoint evolves.
  • Recommend callers pin to a version. Tell the user to call with
    ?version=N
    rather than relying on "latest" — that way a future query edit (which cuts a new version) can't silently change their results. They bump the pinned version deliberately once they've validated the new one.
  • Share friction via
    agent-feedback
    .
    If a limitation gets in the way (eligibility rules, required variables, the TTL enum), send the PostHog team a note — it's how the product and these tools improve.
  • 名称会出现在URL中。后续修改名称需要迁移所有调用方。务必谨慎选择。
  • HogQL端点比洞察端点更灵活。除非用户有特定理由封装现有洞察,否则默认选择HogQL。
  • 无默认值的变量会导致调用失败。创建时务必设置默认值,以便在测试环境中无需指定所有变量即可测试端点。
  • 物化端点要求传入所有变量。未传入变量的调用会被拒绝——这是有意设计的(安全考量:防止返回未过滤的数据)。在推荐物化时,需告知用户哪些变量会变为必填项。(物化端点的可选/部分变量是PostHog团队计划解决的已知限制——若该限制阻碍了用户,可通过
    agent-feedback
    工具反馈。)
  • 不要对不符合条件的查询启用物化。先使用
    endpoints-materialization-preview
    确认是否符合条件,若不符合可查看拒绝原因。
  • 端点并非永久稳定。当用户修改查询时,会自动创建新版本(旧版本仍可通过
    ?version=N
    访问)。
    data_freshness_seconds
    和物化设置是按版本管理的。可随着端点的演进调整这些设置。
  • 建议调用方固定版本。告知用户调用时使用
    ?version=N
    ,而非依赖“最新版本”——这样未来的查询编辑(会创建新版本)不会悄无声息地改变结果。用户可在验证新版本后,主动升级固定的版本号。
  • 通过
    agent-feedback
    反馈问题
    。若遇到限制(如资格规则、必填变量、TTL枚举)阻碍使用,请向PostHog团队发送反馈——这有助于产品和工具的改进。