How to Import Salesforce Custom Metadata Records using CSV/JSON

rrosset91 profile image Roger Rosset ・4 min read

Importing Salesforce Custom Metadata Records can be really tricky. Forget about using import wizard or dataloader to do that. These tools are amazing but simply do not support operations on Custom Metadata Records.

If you perform a quick search on Google about this theme, you probably will, at certain point of your search, get into an guide that will tell you to use the Custom Metadata Loader Loader. This tool may be useful if you have just s few records to import, and none of your records has an special character on it, otherwise you certainly will fail after a lot of bugs and errors.

I've developed an class using the official Salesforce Documentation as guidelines to achieve that requirement, and I'll help you to import your Salesforce Custom Metadata Records following steps below:

1:Prepare your Data

You can use any online service that converts csv into json. I recommend the service below:
Alt Text

Simply upload your CSV file containing the records you want to upload, click on CONVERT and copy the generated JSON.

1.1:Make your JSON inline

What we have to do next, is to parse that JSON into a single line, you can easily do that accessing the following link:

Simply paste your JSON, select COMPACT on the JSON Template Option and click PROCESS. Copy the generated compact JSON and keep that safe for later steps.

2:Create the mdtImport Apex Class using the code below

//author: Roger Rosset
//description: Upload custom metadata records using CSV/JSON

public class mdtImport  {    
    public static void insertMetadata(String metaDataTypeName, String jsonString){
            Integer count = 1;
            Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
            JSONCsvTemplate csv = (JSONCsvTemplate)JSON.deserialize(jsonStringMdt(jsonString), JSONCsvTemplate.class);
            for(JSONCsvTemplate.mdtRecords item : csv.data.mdtRecordsList){
            //Sets the custom metadata type you'll insert your records on

            //If you're using namespaces on your org set it here
            String nameSpacePrefix ='';                                     
            Metadata.CustomMetadata rec =  new Metadata.CustomMetadata();
            String label = 'Record '+count;                      
            rec.fullName = nameSpacePrefix+metaDataTypeName+'.'+label;
            rec.label = label; 

            //Sets the custom metadata custom fields to be inserted
            * Use the template below to setup any fields you want to:
            Metadata.CustomMetadataValue fieldX = new Metadata.CustomMetadataValue();     //New instance
            fieldX.field = 'Custom_Field_Name__c';              //Custom Metadata Field you want to fill
            field1.value = item.JSON_Matching_Key_Value;        //The matching key value on the wrapper
            rec.values.add(fieldX);                             //adds the value and the matching field

            Metadata.CustomMetadataValue field1 = new Metadata.CustomMetadataValue();
            field1.field = 'SubGroup__c';
            field1.value = item.SubGroup;
            Metadata.CustomMetadataValue field2 = new Metadata.CustomMetadataValue();
            field2.field = 'Description__c';
            field2.value = item.Description;
            Metadata.CustomMetadataValue field3 = new Metadata.CustomMetadataValue();
            field3.field = 'keyId__c';
            field3.value = item.keyId;
            Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, null);

            catch(Exception ex){             
            system.debug('Error on insert');

    private static String jsonStringMdt(String jsonString){
        String resultJson = '{"data":{"mdtRecordsList":'+jsonString+'}}';
        return resultJson;

    private class JSONCsvTemplate{
        private class Data{
            private List<mdtRecords> mdtRecordsList;
        private class mdtRecords{
            private String keyId;
            private String SubGroup;
            private String Description;
        private Data data;

As you can see you can execute the methods insertMetaData passing the metaDataTypeName and jsonString as attributes, where metaDataTypeName is the API Name of your Custom Metadata Type (ending with __mdt) and jsonString is your inline JSON that you've copied on the step 1.1, But first you have to adjust the attributes on the JSONCsvTemplate>mdtRecords to fit like your csv database.

3:Adjust the Wrapper class to your requirements

    private class JSONCsvTemplate{
        private class Data{
            private List<mdtRecords> mdtRecordsList;
        private class mdtRecords{
            private String keyId;
            private String SubGroup;
            private String Description;
        private Data data;

As you can see above, we are uploading records that has 3 columns:

These values matches exactly as my csv database headers, and you can add, delete or modify the template to meet your criteria. Keep in mind that your csv database must fit the wrapper class, not only about the field names, but also the primitive data types (Integer, String, Etc.)

Csv Database used:
Alt Text

4:Execute the method

After following all the previous steps and making sure you've created and saved the class on your Salesforce Org, all you have to do next is to execute the insertMetadata
Get your Custom Metadata Type API Name (ending with __mdt) and your JSON String generated in the step 1.1, and execute the method on the Apex Anonymous Window inside the developer console (CTRL+E)
Alt Text

Keep in mind that your JSON String may be really big, and to avoid problems, it's highly recommended you to place the method template and quotes before pasting the "super string" on the parameter inside the method execution, as below:
Alt Text
After preparing the the method, simply paste your big JSON String inside the quotes, and click execute.
Alt Text

If you have checked the Open Log option, you should be taken to the log of this execution, where you can find the ID of your enqueued deploy, and check it's status on the deployment status of Salesforce on:


I've already tested the deploy of 600 records per execution, try to respect that limit to avoid errors.

Feel free to modify the code as you need to, keeping the credits if possible.

Hope it be useful.

Roger Rosset

Based documentation:

Posted on by:

rrosset91 profile

Roger Rosset


Salesforce Developer @Bluewolf IBM


Editor guide