跳到主要内容

v1 API 获取表单数据列表

API使用者,可以通过本接口,获取自己单个表单的数据列表

功能免费版专业版/专业增强版企业基础版企业协作版企业高级版
获取表单数据列表✔️✔️✔️✔️✔️

认证方式​

V1 Bearer 认证方式

headers 设置​

需要在请求中设置如下 headers

  • Content-Type: application/json
  • Accept: application/json
  • Authorization: Bearer YOUR_ACCESS_TOKEN

接口说明​

  • 个人版用户/企业子账号用户,只可以获取 自己创建 的表单数据。无法获取共享的表单。
  • 企业全局 API,可以获取整个企业所有表单的数据。
  • 数据过多时,接口会返回分页数据。每页 50 个。接口会返回下一页的标记。

接口描述​

Request​

# 第一页
GET https://jinshuju.net/api/v1/forms/FORM_TOKEN/entries

# 按字段值过滤(filters 是一个 JSON 字符串)
GET https://jinshuju.net/api/v1/forms/FORM_TOKEN/entries?filters=[{"field":"field_3","operator":"gte","value":6}]

# 关键词全文搜索(一次搜遍表单的所有可搜字段)
GET https://jinshuju.net/api/v1/forms/FORM_TOKEN/entries?keyword=张三

# 如果数据过多,请求后续的数据
GET https://jinshuju.net/api/v1/forms/FORM_TOKEN/entries?next=
参数名称是否必须类型说明
FORM_TOKEN是String表单Token
created_at否String根据时间筛选数据,返回该时间点以后的数据
filters否JSON 数组(也可作为字符串传入)字段值过滤条件,多条件 AND 组合。详见下方「filters 字段过滤」
keyword否String关键词全文搜索,一次搜遍表单的所有可搜字段。详见下方「keyword 全文搜索」
next否String分页参数。原样传回上一页响应中的 next,不要自行构造

注意​

created_at 目前接受两种格式的时间,所有时间均为北京时间,且不支持时区

{
"created_at": "2012-12-25", // 年月日
"created_at": "2012-12-25 12:13:15" // 包含时分秒
}

filters 字段过滤​

filters 用于把字段值条件下推到数据库查询,避免拉全量再本地过滤。每个元素是 {field, operator, value} 三元组,多个条件之间是 AND 关系。

元素类型说明
fieldString字段的 api_code(如 field_3),或系统字段名 created_at
operatorString操作符,见下方表格
value取决于 operator比较值;null / not_null 不需要传

支持的 operator:

类别operatorvalue 形式示例
等值eq / ne标量{"field":"field_1","operator":"eq","value":"张三"}
比较gt / gte / lt / lte标量{"field":"field_3","operator":"gte","value":6}
区间between / not_between2 元素数组 [min, max](闭区间){"field":"field_3","operator":"between","value":[80,100]}
集合any_in / none_in数组{"field":"field_2","operator":"any_in","value":["北京","上海"]}
文本like / not_like子串(不带 SQL 通配符;服务端做不区分大小写的子串匹配){"field":"field_2","operator":"like","value":"张"} 匹配"张三""小张"
是否为空null / not_null省略{"field":"field_4","operator":"not_null"}

operator 与字段类型的兼容性:每种字段类型只接受其支持的 operator(例如 NumberField 接受 gte,TextField 不接受),不匹配会返回 400 并列出当前字段允许的 operator。

字段类型可用 operator
文本类(TextField / NameField / EmailField / MobileField / TelephoneField / IdCardField / LinkField / TextArea)eq ne any_in none_in null not_null like not_like
NumberFieldeq ne null not_null gte gt lte lt between not_between
DateTimeField / DateField / 系统字段 created_ateq ne null not_null like gte gt lte lt between not_between
RatingField / NpsFieldeq ne null not_null gte gt lte lt
RadioButton / CheckBox / DropDowneq ne any_in none_in null not_null like not_like
FormAssociationeq ne any_in none_in null not_null
AttachmentField / GeoField / TableFieldnull not_null like
ESignatureFieldnull not_null

选项类字段(RadioButton / CheckBox / DropDown)的 value 传选项的 api_code(不是中文 label),与 create_entry / update_entry 一致。

排序与分页行为:当 filters 中包含 created_at(或传了 created_at 参数)时,结果按 created_at 升序返回,此时 next 是行偏移;否则按 serial_number 升序,next 是 serial_number 游标。两种情况下客户端都只需把上一页返回的 next 原样传回,不要自行构造。

400 错误响应示例:

// operator 不合法
{ "error_description": "Unsupported operator 'foobar'." }

// operator 与字段类型不匹配
{ "error_description": "Operator 'gte' not supported for field 'field_1' (NameField). Allowed operators: eq, ne, any_in, none_in, null, not_null, like, not_like." }

// between / not_between 必须传 2 元素数组
{ "error_description": "Operator 'between' requires a 2-element array value." }

提示:filters 也可以以 URL 参数传入(重复 filters[] 嵌套结构),但建议使用 JSON 字符串形式以避免歧义。filters 为空数组、null 或非法 JSON 时被静默忽略,等价于不传 filters。

keyword 全文搜索​

不知道值落在哪个字段时用 keyword,一次请求即可搜遍该表单的所有可搜字段——不需要先取字段列表再对每个字段发一条 like 过滤。

  • 覆盖范围与数据页的搜索框一致,不限于文本字段:选项、数字、日期、地址、矩阵、表格子列都在内,按子串匹配存储值。
  • 已知字段时优先用 filters:它把精确的、带类型的条件下推到单列。同时传 keyword 和 filters 时是 AND 关系——既命中关键词、又满足全部过滤条件的数据才会返回。
  • keyword 长度上限 200 个字符,超出返回 400。
  • 数据量超过 99999 条的表单,全文搜索依赖 ClickHouse;该表单不可用时返回 400,请改用 filters 按具体字段过滤。
  • keyword 为空字符串时被静默忽略,等价于不传。

400 错误响应示例:

// 关键词过长
{ "error_description": "Keyword is too long (201 characters, at most 200)." }

// 表单过大且全文搜索不可用
{ "error_description": "Full-text search is unavailable on this form: it holds more than 99999 entries. Filter on a specific field instead." }

Response​

{
"total": 828,
"count": 50,
"data": [
{
"serial_number": 1,
"field_1": "张三",
"field_2": "13000000000",
"info_filling_duration": 28,
"creator_name": "",
"created_at": "2020-08-28T08:00:00.000Z",
"updated_at": "2020-08-28T08:00:00.000Z"
},
{
"serial_number": 2,
"field_1": "李四",
"field_2": "13000000001",
"info_filling_duration": 23,
"creator_name": "子账号",
"created_at": "2020-08-28T08:00:00.000Z",
"updated_at": "2020-08-28T08:00:00.000Z"
}
],
"next": 51
}
参数名称是否必须类型说明
total是Number表单数据总数
count是Number本次请求返回的数据数量
data是Array数据数组
data[].serial_number是String数据序号(标识符)
data[].field_*是对应字段的值
data[].info_filling_duration否Number本条数据的填写时长(Excel导入、API提交,此值为空)
data[].creator_name否String本条数据的填写者(仅对系统内录入数据有效)
data[].created_at是Date数据提交时间
data[].updated_at是Date数据最后一次变更时间
next否String分页参数。原样传回即可请求下一页数据;null 表示没有下一页

数据详细结构,请参考文档

注意:数据中的日期时间,使用的是 UTC 时间(例如:"2021-09-28T02:59:42.539Z"),接受者需要自行转换为自己所需要的时区(例如北京时间)

示例代码​

伪代码​

access_token = "YOUR_ACCESS_TOKEN"

auth_header_payload = "Bearer " + access_token

headers = {"Authorization": auth_header_payload}

form_token = "YOUR_FORM_TOKEN"

http.get("https://jinshuju.net/api/v1/forms/${form_token}/entries", headers)

HTTP​

GET https://jinshuju.net/api/v1/forms/$FORM_TOKEN/entries

Content-Type: application/json
Accept: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

Postman​

GET https://jinshuju.net/api/v1/forms/$FORM_TOKEN/entries

authorization 选择 `Bearer Token`

Token 输入 Access Token

Java​

package net.jinshuju.v1api.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class GetFormEntriesService {

public void run(String accessToken, String formToken) throws IOException {
String authHeaderPayload = "Bearer " + accessToken;

BufferedReader httpResponseReader = null;
try {
URL apiEndpointUrl = new URL("https://jinshuju.net/api/v1/forms/" + formToken + "/entries");
HttpURLConnection urlConnection = (HttpURLConnection) apiEndpointUrl.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.addRequestProperty("Authorization", authHeaderPayload);

httpResponseReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
String lineRead;
while ((lineRead = httpResponseReader.readLine()) != null) {
System.out.println(lineRead);
}
} finally {
if (httpResponseReader != null) {
try {
httpResponseReader.close();
} catch (IOException ignored) {
}
}
}
}

}

Python​

import requests

access_token = 'YOUR_ACCESS_TOKEN'

form_token = 'YOUR_FORM_TOKEN'

api_endpoint_url = 'https://jinshuju.net/api/v1/forms/' + form_token + '/entries

response = requests.get(api_endpoint_url, headers = {'Authorization': f'Bearer {access_token}'})

print(response.text)

Ruby​

require 'net/http'
require 'uri'

form_token = 'YOUR_FORM_TOKEN'

uri = URI.parse("https://jinshuju.net/api/v1/forms/#{form_token}/entries")
access_token = 'YOUR_ACCESS_TOKEN'

request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{access_token}"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end

puts(response.body)