diff --git a/README.md b/README.md
index 5cc6c7d5d273906b2bc680ae9b8013e6d388a7dd..0e476a6be06832eb25a7dd84a08dc0077e57cf49 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ The core of Mol* currently consists of these modules:
 - `mol-math` Math related (loosely) algorithms and data structures.
 - `mol-io` Parsing library. Each format is parsed into an interface that corresponds to the data stored by it. Support for common coordinate, experimental/map, and annotation data formats.
 - `mol-model` Data structures and algorithms (such as querying) for representing molecular data (including coordinate, experimental/map, and annotation data).
+- `mol-model-props` Common "custom properties"
 - `mol-script` A scriting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)).
 - `mol-geo` Creating molecular geometries.
 - `mol-gl` A wrapper around WebGL. Uses `mol-geo` to generate geometries.
diff --git a/package.json b/package.json
index 40e7655c3b9d40d5d2ccc189c6a249d80c157895..f0d17b42e09f3d61c5595a8d57ac5ef54b9df5d4 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
       "mol-io($|/.*)": "<rootDir>/src/mol-io$1",
       "mol-math($|/.*)": "<rootDir>/src/mol-math$1",
       "mol-model($|/.*)": "<rootDir>/src/mol-model$1",
+      "mol-model-props($|/.*)": "<rootDir>/src/mol-model-props$1",
       "mol-ql($|/.*)": "<rootDir>/src/mol-ql$1",
       "mol-script($|/.*)": "<rootDir>/src/mol-script$1",
       "mol-task($|/.*)": "<rootDir>/src/mol-task$1",
diff --git a/src/servers/model/properties/structure-quality-report.ts b/src/mol-model-props/pdbe/structure-quality-report.ts
similarity index 92%
rename from src/servers/model/properties/structure-quality-report.ts
rename to src/mol-model-props/pdbe/structure-quality-report.ts
index e46fb5fdce65112f686397031dafa68a66aae33d..98d0f440f0f3ca976e5f16677a88d94cc08b7fe8 100644
--- a/src/servers/model/properties/structure-quality-report.ts
+++ b/src/mol-model-props/pdbe/structure-quality-report.ts
@@ -8,7 +8,6 @@ import { Segmentation } from 'mol-data/int';
 import { CifWriter } from 'mol-io/writer/cif';
 import { Model, ModelPropertyDescriptor, ResidueIndex, Structure, StructureElement, Unit } from 'mol-model/structure';
 import { residueIdFields } from 'mol-model/structure/export/categories/atom_site';
-import { fetchRetry } from '../utils/fetch-retry';
 import CifField = CifWriter.Field;
 
 type IssueMap = Map<ResidueIndex, string[]>
@@ -95,12 +94,17 @@ function createIssueMap(modelData: Model, data: any): IssueMap | undefined {
 export namespace StructureQualityReport {
     export const Descriptor = _Descriptor;
 
-    export async function attachFromPDBeApi(model: Model) {
+    export async function attachFromCifOrApi(model: Model, params: {
+        // provide JSON from api
+        pdbEapiSourceJson?: (model: Model) => Promise<any>
+    }) {
+        // TODO: check if present in mmCIF on Model
+        if (!params.pdbEapiSourceJson) throw new Error('Data source not defined');
+
         if (model.customProperties.has(Descriptor)) return true;
 
         const id = model.label.toLowerCase();
-        const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5);
-        const json = await rawData.json();
+        const json = await params.pdbEapiSourceJson(model);
         const data = json[id];
         if (!data) return false;
         const issueMap = createIssueMap(model, data);
diff --git a/src/servers/model/properties/rcsb/graphql/symmetry.gql.ts b/src/mol-model-props/rcsb/graphql/symmetry.gql.ts
similarity index 100%
rename from src/servers/model/properties/rcsb/graphql/symmetry.gql.ts
rename to src/mol-model-props/rcsb/graphql/symmetry.gql.ts
diff --git a/src/servers/model/properties/rcsb/graphql/types.ts b/src/mol-model-props/rcsb/graphql/types.ts
similarity index 100%
rename from src/servers/model/properties/rcsb/graphql/types.ts
rename to src/mol-model-props/rcsb/graphql/types.ts
diff --git a/src/servers/model/properties/rcsb/symmetry.ts b/src/mol-model-props/rcsb/symmetry.ts
similarity index 100%
rename from src/servers/model/properties/rcsb/symmetry.ts
rename to src/mol-model-props/rcsb/symmetry.ts
diff --git a/src/servers/model/properties.ts b/src/servers/model/properties.ts
index 093b01d89afa7f14e410f3c1a97d4c840e5fbc91..95d1ade02545101bc935dd7644540de94f0ddf8f 100644
--- a/src/servers/model/properties.ts
+++ b/src/servers/model/properties.ts
@@ -6,14 +6,13 @@
  */
 
 import { Model } from 'mol-model/structure';
-import { StructureQualityReport } from './properties/structure-quality-report';
-// import { SymmetryAnnotation } from './properties/rcsb/symmetry';
+import { PDBe_structureQualityReport } from './properties/pdbe';
 
 export function attachModelProperties(model: Model): Promise<any>[] {
     // return a list of promises that start attaching the props in parallel
     // (if there are downloads etc.)
     return [
-        StructureQualityReport.attachFromPDBeApi(model),
+        PDBe_structureQualityReport(model)
         // removed for now because of schema validation error
         // SymmetryAnnotation.attachFromRCSB(model)
     ];
diff --git a/src/servers/model/properties/pdbe.ts b/src/servers/model/properties/pdbe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ae5d369f826c481ff9570562d600a5759cc008d
--- /dev/null
+++ b/src/servers/model/properties/pdbe.ts
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+ import { Model } from 'mol-model/structure';
+import { StructureQualityReport } from 'mol-model-props/pdbe/structure-quality-report';
+import { fetchRetry } from '../utils/fetch-retry';
+
+export function PDBe_structureQualityReport(model: Model) {
+    return StructureQualityReport.attachFromCifOrApi(model, {
+        pdbEapiSourceJson: async model => {
+            const rawData = await fetchRetry(`https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry/${model.label.toLowerCase()}`, 1500, 5);
+            return await rawData.json();
+        }
+    });
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 48fb8c0dc68e8ec29fb848888ed98531fc1cdf85..bc8a5e19351f9ef7428c9ce770c6b938287749ae 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -23,6 +23,7 @@
             "mol-io": ["./mol-io"],
             "mol-math": ["./mol-math"],
             "mol-model": ["./mol-model"],
+            "mol-model-props": ["./mol-model-props"],
             "mol-ql": ["./mol-ql"],
             "mol-script": ["./mol-script"],
             "mol-task": ["./mol-task", "./mol-task/index.ts"],