Skip to content

Table Operation: Import Record

Overview

Import records into the specified table using the API.

Preparations

Please Create an API Key before performing API operations.

Request

Send CSV data and JSON parameters using the request format below.

Setting column Value
HTTP Method POST
Content-Type multipart/form-data
Character Code UTF-8
URL http://{server name}/api/items/{site ID}/import (*1)
Body Refer to "Column to Specify for Body" below

(*1) Please edit the {server name} and {site ID} parts suit your environment as appropriate.
  For Pleasanter.net, the format is as follows:
  https://pleasanter.net/fs/api/items/{site ID}/import

Column to Specify for Body

Column name Value
parameters Specify the contents of the "API Parameters" below as a JSON format string
file Binary data of the CSV file to be registered

API Parameters

Column name e.g. Notes
ApiVersion 1.1 API version
ApiKey 3da0fa3a7R61faf821... Acquired API key
Encoding Shift-JIS Encoding of CSV file. Specify "UTF-8" or "Shift-JIS"
UpdatableImport true Specify true if you want to "update records with matching keys"
Key IssueId Key column when UpdatableImport is set to true. If the key column is ClassA, set it to "ClassA"

Execution Example

If the response is a 403 error after executing the following sample, even though the API key belongs to a valid user and has write permission to the target site, please refer to the following FAQ.
FAQ: 403 Error Occurs When Importing Records via API Using PowerShell

PowerShell (version 6.0 or later) Sample

PowerShell
$uri = 'http://servername/api/items/1234/import'
$filePath = "./sample.csv"
$form = @{
    parameters = ConvertTo-Json @{
        ApiVersion = 1.1;
        ApiKey = "4d84b4773a58bbc3c4...";
        Encoding = "UTF-8";
        UpdatableImport = $true;
        Key = "IssueId";
    };
    file = Get-Item -Path $filePath;
}
Invoke-WebRequest -Uri $uri -Method Post -Form $form

Python Sample

Python
import requests
import json

url = "https://servername/api/items/1234/import"
filePath = "./sample.csv"
data = {
    "parameters": json.dumps({
        "ApiVersion" : 1.1,
        "ApiKey" : "4d84b4773a58bbc3c4...",
        "Encoding" : "UTF-8",
        "UpdatableImport" : True,
        "Key" : "IssueId"
    })            
}
files = {
    "file":("sample.csv", open(filePath,"rb"), "text/csv")
}
response = requests.post(url, data, files=files)
print(response.content.decode())

Response

The json data in the following format will be returned.

JSON
{
  "Id":1234,
  "StatusCode":200,
  "LimitPerDate":1000,
  "LimitRemaining":998,
  "Message": "Table Name: 50 added, 12 updated."
}

Code Samples

Modify 【 ... 】 in the code as necessary.
1. Register images along with a CSV import
Overview

This is a sample batch program for migrating images and data managed in Excel to Pleasanter. Along with the CSV import of the text information, it also registers the images in the records.

Process Flow

Step 1 - CSV import of the text information

Register the file exported from Excel as CSV in bulk with the import API.
Records whose key column matches are updated, so it is safe to run it again.

Step 2 - Get the record IDs

Get the imported records via the API, and associate the values of the key column with the Pleasanter record IDs.
Based on this mapping, Step 3 decides which records to update.

Step 3 - Register the embedded images

Scan the image folder, and register the images for each record with the API.
The record is identified by the folder name, and the column to register to is determined by the file name.

Rules for Folder Names / File Names and Image Registration

Folder structure

images/
  {Key code}/
    body.jpg
    desc_a.png
    desc_b.jpg
    desc_c.png
    comments.jpg

Folder name rules

Directly under the image folder, create folders with the same names as the values of the key column in the CSV file.

Folder name Corresponding record
P-001 The record whose key column value is P-001
P-002 The record whose key column value is P-002

The key column is defined in KEY_COLUMN in the sample program.
(In this sample code, 'ClassB')

File name rules

For the image files in a folder, the column to register to is determined by the file name (without the extension).

File name Column to register to
body Body
desc_a DescriptionA
desc_b DescriptionB
desc_c DescriptionC
comments Comments
  • The extensions that can be used are .jpg .jpeg .png .gif
  • File names other than the above are skipped
  • Columns that do not need registration are not processed if you do not put a file for them
Sample Code
import base64
import json
import os
from pathlib import Path

import requests

# =========================
# Settings
# =========================
BASE_URL = "【URL】"
API_KEY = "【API key】"
SITE_ID = 【Site ID】  # Site ID of the site to import into

# CSV import settings
CSV_PATH = Path("./sample.csv")  # Path of the CSV file to import
CSV_ENCODING = "UTF-8"  # Encoding of the CSV file ("UTF-8" or "Shift-JIS")
KEY_COLUMN = "ClassB"  # Key column of the CSV file (such as a management number). Used for matching in GetItems

# Root path of the image folder
IMAGES_DIR = Path("./images")

# "IssueId" or "ResultId"
RECORD_ID_FIELD = "ResultId"

# =========================
# Common processes
# =========================
# Mapping between file name prefixes and ImageHash keys (Pleasanter column names)
FILE_PREFIX_MAP = {
    "body": "Body",
    "desc_a": "DescriptionA",
    "desc_b": "DescriptionB",
    "desc_c": "DescriptionC",
    "comments": "Comments",
}

SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif"}


def post_json(url: str, payload: dict) -> dict:
    response = requests.post(
        url,
        headers={"Content-Type": "application/json"},
        data=json.dumps(payload, ensure_ascii=False),
        timeout=60,
    )
    response.raise_for_status()
    return response.json()


def post_file(url: str, params: dict, file_path: Path) -> dict:
    with open(file_path, "rb") as f:
        response = requests.post(
            url,
            data={"parameters": json.dumps(params, ensure_ascii=False)},
            files={"file": (file_path.name, f, "text/csv")},
            timeout=120,
        )
    response.raise_for_status()
    return response.json()


def encode_image(file_path: Path) -> str:
    """Encode an image file in Base64 and return it as a string"""
    return base64.b64encode(file_path.read_bytes()).decode("utf-8")


# =========================
# Step 1: CSV import
# =========================
def step1_import_csv() -> None:
    print("=" * 50)
    print("Step 1: CSV import")
    print("=" * 50)

    url = f"{BASE_URL}/api/items/{SITE_ID}/import"
    params = {
        "ApiVersion": 1.1,
        "ApiKey": API_KEY,
        "Encoding": CSV_ENCODING,
        "UpdatableImport": True,  # Update records whose key matches
        "Key": KEY_COLUMN,
        "MigrationMode": False,
    }

    result = post_file(url, params, CSV_PATH)
    print(f"  Status  : {result.get('StatusCode')}")
    print(f"  Message : {result.get('Message')}")


# =========================
# Step 2: Get the record IDs
# =========================
def step2_get_record_ids() -> dict:
    """
    Get all the imported records, and return a map of key column value -> record ID.
    If there are many records, get all of them with paging.
    """
    print("=" * 50)
    print("Step 2: Get the record IDs")
    print("=" * 50)

    url = f"{BASE_URL}/api/items/{SITE_ID}/get"
    key_to_id = {}
    offset = 0
    page_size = 200

    while True:
        payload = {
            "ApiVersion": 1.1,
            "ApiKey": API_KEY,
            "Offset": offset,
            "PageSize": page_size,
        }

        result = post_json(url, payload)
        records = result.get("Response", {}).get("Data", [])

        if not records:
            break

        for rec in records:
            # Supports both the ClassHash format and direct specification
            class_hash = rec.get("ClassHash", {})
            key_val = class_hash.get(KEY_COLUMN) or rec.get(KEY_COLUMN)
            record_id = rec.get(RECORD_ID_FIELD)
            if key_val and record_id:
                key_to_id[str(key_val).strip()] = record_id

        if len(records) < page_size:
            break
        offset += page_size

    print(f"  Retrieved : {len(key_to_id)} records")
    return key_to_id


# =========================
# Step 3: Update the embedded images
# =========================
def build_image_hash(key_dir: Path) -> dict:
    """
    Scan the image files in the key directory and build a dictionary for ImageHash.
    Files that are not targets are ignored.
    """
    image_hash = {}

    for file in sorted(key_dir.iterdir()):
        if not file.is_file():
            continue
        if file.suffix.lower() not in SUPPORTED_EXTENSIONS:
            continue

        # Identify the column to register to by the file name prefix
        prefix = file.stem.lower()
        target_field = FILE_PREFIX_MAP.get(prefix)
        if target_field is None:
            print(f"    [SKIP] Not a target file name: {file.name} (skipped)")
            continue

        image_hash[target_field] = {
            "HeadNewLine": True,
            "EndNewLine": True,
            "Extension": file.suffix.lower(),
            "Base64": encode_image(file),
        }
        print(f"    {file.name} → {target_field}")

    return image_hash


def step3_update_images(key_to_id: dict) -> None:
    print("=" * 50)
    print("Step 3: Update the embedded images")
    print("=" * 50)

    if not IMAGES_DIR.exists():
        print(f"  The image folder was not found: {IMAGES_DIR}")
        return

    success = skip = error = 0

    for key_dir in sorted(IMAGES_DIR.iterdir()):
        if not key_dir.is_dir():
            continue

        key_val = key_dir.name
        record_id = key_to_id.get(key_val)

        if record_id is None:
            print(f"  [SKIP] No record was found for the key '{key_val}'")
            skip += 1
            continue

        print(f"  Key: {key_val}  →  Record ID: {record_id}")
        image_hash = build_image_hash(key_dir)

        if not image_hash:
            print(f"    [SKIP] There are no image files to register")
            skip += 1
            continue

        payload = {
            "ApiVersion": 1.1,
            "ApiKey": API_KEY,
            "ImageHash": image_hash,
        }

        url = f"{BASE_URL}/api/items/{record_id}/update"
        try:
            result = post_json(url, payload)
            if result.get("StatusCode") == 200:
                print(f"    [OK] Updated")
                success += 1
            else:
                print(
                    f"    [ERR] StatusCode={result.get('StatusCode')}  {result.get('Message')}"
                )
                error += 1
        except requests.HTTPError as e:
            print(f"    [ERR] HTTP error: {e}")
            error += 1

    print()
    print(f"Completed  Success={success}  Skipped={skip}  Error={error}")


# =========================
# Main
# =========================
def main():
    step1_import_csv()
    print()
    key_to_id = step2_get_record_ids()
    print()
    step3_update_images(key_to_id)


if __name__ == "__main__":
    main()

Confirmation Column in Case of Error

・Precautions when using the API and things to check if an error occurs
・FAQ: What to check if modified configuration files or API requests (JSON format) are not recognized correctly

Supported Versions

Supported versions Body
1.4.16.0 and later Added the MigrationMode parameter